Second way to correct the application

We have seen in the previous example that it was possible to replace a get() by a rd() operation in order to write rules where the availability of given resources is verified without consuming it, allowing to use it within other instances of the rule or within other rules.

However, in our case, the availability of a connection is permanent: a connection in a metro network is part of the definition of this precise network. It should not get consumed by any customer or train using it. We may think of other scenarios where the connection is no longer available in the event of maintenance, but this does not match our use case.

Here, the permanence of the connections is a main characteristic of all the resources contained in the connection bag. All connection resources are permanent. This kind of intrinsic behavior can be specified within the bag design description of the test object.

Changing the behavior of a bag

Remember: the Connection bag, in the initial application description file, was defined as follow :

 bag2 = data.BagDescription() = "Connection"
 bag2.type = ("Multiset", "TupleSpace")
 bag2.fieldnames = ['departure', 'arrival']
 bag2.params = {}

We declared a classical bag of type Multiset. It is possible to refine this type by using one of the aspects existing for this type of bag. A full description of the distinct aspects of a Multiset bag is given in section Aspects


Here, as our resource should not get consumed, we are interested by the ReadOnly aspect. This aspect allows specifying whether or not the resource is effectively removed from the bag when the get() is confirmed in a transaction. Then, it allows a classical bag to be changed to make it behave as if it contained an infinite number of any of its resources (as if a given resource is immediately replaced by an identical one, as soon as the first one gets consumed by the get operation).

This is exactly what we need for our Connection bag.

Consequently, the description of the Connection bag is adapted as :

 bag2 = data.BagDescription() = "Connection"
 bag2.type = ("Multiset", "TupleSpace")
 bag2.fieldnames = ['departure', 'arrival']
 bag2.params = {param.ASPECT_READONLY:Truep}     <--   We set the value of ReadOnly aspect to True

Now, we can use the original first script remaining hereafter :

 {*,!}["Test","Customer"].rd(name,departure) &
 {*,!}["Test","Destination"].rd(name,arrival) &
 ["Test","Connection"].get(departure,arrival); <--------/ we consume one instance among the infinite number of available instances
 ["Test","Customer"].put(name,arrival) ;

Let's start this new version of the corrected program, using the same command as in the lesson 03 which combines the launch of the object and the execution of all the scripts:

python --go

If we inspect the content of the Test object, we get something like this:

| Customer| Destination| Connection| Travel| |---|---|---|---|---| | ('bob','D')| ('denis','D')| ('A','B')| ('A','D','alice')| | ('denis','A')|| ('A','D')| ('C','D','charles')| | ('alice','D')||| ('A','D','bob')| | ('charles','D')|||

Once again, the results are correct and the global behavior of the application is the same as described in the lesson 03. On a pure internal point of view, exactly the same operations have been done.

As a conclusion on this subject, we have the choice between two distinct ways to ensure a read only behavior on some resources. It is up to the programmers to decide if this behavior has to be done with a bag aspect or explicitly in the rule.

As a rule of thumb :

  • If we want to have the choice to consume or not the resources, it is required to do it at the rule level.
  • If this is a permanent behavior we want to enforce, it is better to put it at bag level.

More about the aspects of a bag

Basically, thanks to the predefined optional aspects, we can modify/adapt the behavior of a bag. From an internal point of view, setting a given aspect is a customization of the standard real effect of one of the operations of this middleware protocol.

As the enactment of a rule blindly invokes the operations of the protocol in a given order, no particular hypothesis is made by the system on the real effect of these operations. This ensures a great flexibility and allows in particular the possibility to modify the actual effect of an operation.

Here, we have explicitly set the ReadOnly aspect of the Connection bag. However, the system is also using two other aspects in the NameServer object. If you look at this object through the monitor interface, you can see 2 bags: Wait, NoWait.

The Wait bag contains resources that are inserted by the objects themselves when they are launched. This include, among other things, the data structures used internally by the system to reach the bags involved in the rules.

A resource of the bags Wait and NoWait are defined by the following fields:

  • a logical name of an object or a bag (in this case the concatenation of the object name and the bag name)
  • a resource type, which is the constant 'BAG' in case of a bag or 'OBJECT'
  • an access_pattern field, mainly composed by a serialized stub that gives a means to reach the bag.

When a bag has to get reached during the enactment of a rule, a rd() operation is done against the Wait bag. The result of this rd() operation is a resource containing a stub which can be used by the system to invoke operations on the remote given bag (More information about the stub can be find here stub).

Here, the important point is that the description of the Wait bag includes the KeyLength aspect. This aspect is used to define the length of a key associated with the bag. A key, if defined, is a resource identifier and its value is unique within all the resources contained in the given bag. A key is composed of the first fields of the resource, following the design order in the bag description. The KeyLength aspect sets the number of fields used to define the key (1 or more, to get an identifier). In the case of the Wait bag, KeyLength is set to 1. This means that the first field of the resources is a key. So, we cannot have two resources with the same first field value (here : logical_name) in this bag.

As an example, this aspect is very convenient for the Wait bag when we want to launch an already existing object in a new location, while the application is running (this is called the relocation of an object). Actually, as soon as the 'new' object is up and running (with the same object name but running in another new location), it performs put() operations against the name server to register the resources corresponding to its bags. Since these resources have the same key values as those introduced by the initial object (same logical_name values), the new resources overwrite the older ones. From then, only the 'new' resources will be available and used in our applications. The old object can be stopped and destroyed, as the system has automatically switched to the new one.

The bag is called Wait because the rd() operation obeys to the normal (default) blocking aspect when a searched resource is not available. This is very convenient when the application is launched, because it forces the system to wait for the start of all the objects involved in a rule before enacting it. This behavior ensures - for free - an automatic synchronization between the object and the rule's enactment.

However, sometimes it could be useful to have a bag that is not blocking when the resource is not present. This aspect is activated within the NoWait bag. This bag shares the same resources than the Wait one, but provides with a different aspect (i.e. NonBlocking) for the rd() operation. In this case, if the searched resource is not present the rd() returns a no-more-resource result. The system can then take a decision because it knows that the object/bag is not currently available. If it were blocked, nothing could be done.

The full details about the aspects of the MultiSet bags may be found in the section Aspects


As in the lesson 03, we have seen here another way to correct our initial buggy application.