6.4 Persistence, Logging and Propagation at the Manager
The previous sections described the cache handling side of LLAPI.
Now we describe the available features at the manager as it commits the transaction. It can:
- Persist to one or more datastores, perserving atomicity and isolation
- Propagate the changed values in the cache, for example to provide replication to another data centre
- Log transactions as the occur, so the changes can be recreated after a complete outage.
Here's the diagram:
- Commit One client starts the transaction and may enrol other clients to do work in the transaction.
When a client makes changes in the grid, these are noted in the client.
When the client commits his part of the work, CloudTran sends his changes to the manager.
When the last commit arrives at the manager, CloudTran starts committing the transaction.
- Transformer CloudTran supports persistence direct from the manager, propagating isolation to the datastores.
In general, the persistable objects are a different set from the grid objects. There are two reasons for this.
First, transient objects (workflow statuses, or in-memory counters) and messages to or from a messaging subsystem
do not need to be persisted; the transformer should omit these.
Second, if a grid object contains nested objects that will go into different datastore transactions,
then these will need to be broken apart.
This situation may arise when you can store aggregates in the grid - a master object along with its dependents.
For example, in a two-level aggregation, the master object could be Order and the dependent objects instances of OrderLine.
Higher-level aggregations are possible - Customer, Orders and OrderLines could be a three-level aggregate.
If all the constituent objects from an aggregate go to the same datastore - so they can all be written in the same transaction -
and no other type of transaction will write one of the objects,
they do not need to be split apart - the persistent object is the same as the grid object.
If the objects will go to different datastores or if other use cases will write some of the constituent objects.
You can specify a transformer object to CloudTran in the
property. If you don't specify a transformer, then the persistable object list will be the same as the grid object list.
We now have two lists of objects: the grid objects and the persistable objects.
- PreCommit Event
An application can register a ManagerEventListener using
CTx.addListener to receive event notifications during the commit process
The notification calls contain a ManagerEvent object, which contains the grid and persistable objects.
The first notification - PreCommit - happens at this point.
This is called 'PreCommit' because at this point, the transaction can still be aborted: it has not reached COMMITTING state yet.
You may find a PreCommit useful if there is a reason to do further validation of the grid and persistable objects.
Of course, this is a last resort: doing validation in the grid in EntryProcessors or MapTriggers is much more efficient.
The listener can abort the transaction by throwing a TransactionExceptionNonRetriable exception
(probably a TransactionExceptionParameterOrUsageError) during PreCommit event handling.
If the transaction is aborted, none of these events is triggered.
The PreCommit event is called synchronously on the main path of the manager,
so any lengthy operation should be avoided.
- Isolator call
The Manager now calls the Isolator, to determine if the transaction is OK to persist.
It may not be, because there are other transactions that have overlapping persistable objects that have not persisted yet.
This call happens before the "Committing" event, because there is a remote possibility that the Isolator
will throw an exception. This only happens in fatal error situations.
- Committing Event
This is the second ManagerEventListener notification. It signals that the transaction has reached the point of no return:
it must carry on and commit.
If your event handler throws an exception, it is caught by the manager and logged, but it will not stop the commit process.
The Committing event is called synchronously on the main path of the manager,
so any lengthy operation should be avoided, or handled asynchronously on another thread if unavoidable.
This event is noted "Messaging/Grid Replication". It is the place to send messages
to external messsaging endpoints and, in particular, to replicate transactions from this grid to another grid.
The "gridObjects" list in the ManagerEvent contains a complete transaction.
This can be injected into another grid using the replicator.
To ensure that order is preserved during transmission (so a later transaction does not overtake a newer one)
the messaging system used should preserve order and the write to the messaging system should be done before the transaction commits into the grid.
This will guarantee that the grid objects will still be locked in the grid (by the current transaction)
and cannot be overtaken by another transaction.
The easiest way to guarantee this order is to do a synchronous write from the manager to the messaging subsystem.
However, this will cause a delay to the main manager path.
Therefore, the manager provides a way to avoid this. It works as follows:
the Committing event handler can operate asynchronously return a Semaphore that will be evented
- The manager calls the commit event handler.
- The event handler creates a semaphore with 0 permits and puts the list of grid objects and the semaphore on a queue.
- The handler returns the semaphore to the manager.
- The manager proceeds with other work. Before committing to the grid, it tries to acquire the semaphore.
This may cause the manager to wait for the messaging subsystem.
- The thread servicing the queue sends the grid objects to the messaging subsystem.
When the messaging subsystem has transmitted the message, or in some other way guaranteed that the message will be transmitted,
it releases the semaphore.
- The manager acquires the semaphore. It can then commit the grid objects into the grid.
- Commit process This starts three parallel activities.
First, the manager sends a 'commit' instruction to the caches holding transactional entries for the current transaction,
based on the 'gridObjects' in the ManagerEvent.
If logBefore Commit is set, either in the default configuration property
or in starting a transaction with the CTxDefinition
then the commit instructions are held until the logger entry is written.
Second, the manager calls the isolator to make sure that it is OK to persist this transaction now,
based on the 'gridObjects' in the ManagerEvent.
Normally it will be, but if there are other transactions persisting objects that overlap with this transaction, it will wait
until those objects have been persisted.
Finally, the manager calls the logger (as long as one of logBeforeCommit or logAfterCommit is set for this transaction), to
make a local persistent copy of the 'gridObjects' in the ManagerEvent.
The transaction logger is used to recover the latest transctions in case of a complete failure of the local grid.