by geoff >> Mon, 18 Mar 2002 22:21:15 GMT
Take an order entry system as an example. The root object is likely to have the following collections:
* allCustomers
* allProducts
* allOrders
All the collections are large but 'allOrders' could potentially become a bottleneck if orders come in thick and fast. The bottleneck would result in lock exceptions on the clients as they all attempt to update the 'allOrders' collection at the same time.
I first look at a typical system design that does nothing about this bottleneck, then suggest an alternative approach.
A typical design might have the following references defined in the Order class:
* myCustomer (with an inverse allOrders in the Customer class)
* myProduct (with an inverse allOrders in the Product class)
* myRoot (with an inverse allOrders in the Root class)
The first two references make a lot of sense in terms of the model and make good use of JADE's automatic inverse maintenance. I am less happy about every Order object (and every Product and Customer object) having a 'myRoot' reference. Why set the 'myRoot' reference of an object when the target can be guaranteed to be 'app.myRoot'? The justification is to invoke JADE's automatic inverse maintenance. However, why not simply add the object directly to the collection on the Root object.
What I am suggesting is replacing the following instruction in the method that sets initial values for an Order
self.myRoot := app.myRoot;
with
app.myRoot.allOrders.add(self);
To cater for deletions of Order objects, the following instruction is needed in the destructor
app.myRoot.allOrders.delete(self);
The previous suggestion does nothing to alleviate the bottleneck caused by the transaction that creates an Order object having to update the 'allOrders' collection on the Root object.
The alternative approach I am suggesting separates the creation of an Order object from the related update to the 'allOrders' collection. With this approach, a client can create an Order object in parallel with other clients because there is no single-threading caused by contention over the 'allOrders' collection on the Root object.
A background task running on the server will fix up the 'allOrders' collection by adding the new Order objects as quickly as it can. A drawback to this approach is that the collection is not always up-to-date. That is the price you pay for increased parallelism. How critical is a slight lag in updating the collection? That is the question you must answer before adopting this approach.
Here is an outline of a possible implementation of this alternative approach.
When an Order object is created, instead of adding to the 'allOrders' collection, create an instance of a new class called 'AddToOrdersCollection'. The 'AddToOrdersCollection' instance would have a reference called 'order' to the newly created order.
A background task on the server would periodically create a virtual collection 'AddToOrdersCollection.instances' to see if any orders need adding to the 'allOrders' collection. When an order is added, the corresponding instance of 'AddToOrdersCollection' would be deleted. Note the use of a virtual collection 'AddToOrdersCollection.instances' (rightly frowned upon in a production system). But if a proper collection of instances of AddToOrdersCollection were maintained and used for this purpose, we would simply be substituting contention on this new collection for contention on the 'allOrders' collection.