Monday, July 31, 2006

OMG Object Transaction Service

Transactions Series Part 4

This specification is based on Distributed Transaction Processing: The XA Specification, X/Open Document C193 (http://www.opengroup.org/onlinepubs/9698909699/toc.pdf). The difference ofcourse being that the interface is object oriented and IDL based.

Architecture

At the heart of this specification is the concept of a transactional object which is any CORBA object whose behavior guarentees ACID properties. In simpler words, its changes can be recovered and rolled back. The key players in this technology are -

  • Transaction client - A CORBA client which initiates a transaction and makes use of or calls on Transactional objects
  • Transactional Object - A CORBA object whose changes can be recovered and rolled back.
  • Recoverable Object - A transactional CORBA object which hosts data and can take participate in transaction protocols such as 2PC
Application Programming Model

Application programming using OTS interfaces depends on -

  • Context Management
  • Context Propagation
Context Management

Two kinds of context management is possible using OTS interfaces -

  • Direct Context Management - Context is managed using explicity OTS interfaces such as Control object.
  • Indirect Context Management - Thread specific Current interface is used for context maangement
Context Propagation

Propagation is the mechanism of associating the client's transaction context with the target object on which invocation is to be made. This could be -

  • Implicit - context is transparently propagated to the target object
  • Explicit - context is explicitly propagated to the target object using explicit parameters in the method call and using OTS interfaces.
Thus, using Direct and Indirect Context management and Implicit and Explicit Propagation fours styles of programming models is possible.

Checked Transactions

The value of checked transactions is to maintain the transactional integrity. The bottomline of checked transactions is that commit will not succeed unless all transactional objects involved in the transaction have completed the processing of their transactional requests.

There are apparently many ways of achieving this, but one commonly used mechanism is as illustrated by X/Open Checked Transactions.

X/Open Checked Transaction

A prime requirement in this pattern is that the application programming model has to be Implicit Transaction Context propagation. When implicit propagation is used, the objects involved in a transaction at any given time may be represented as a tree, the request tree for the transaction. The beginner of the transaction is the root of the tree. Requests add nodes to the tree, replies remove the replying node from the tree.

Synchronous requests, automatically ensures that the tree collapses to a single point before commit is issued. In addition, transaction manager applies resume check to ensure that the transaction is only resumed by application programs in the correct part of the request tree. I think because of resume checking, an invocation in a correct request path cannot create another thread, and then expect the created thread to resume the transaction, even though it would want to wait for the sub thread to complete its operation.

If a transaction uses explicit propagation, the Transaction Service cannot know which objects are or will be involved in the transaction; that is, a request tree cannot be constructed or assured. Therefore, the use of explicit propagation is not permitted by a Transaction Service implementation that enforces X/Open-style checked behavior.

With deferred synchronous calls, certain other conditions need to be checked. These are -
  • Reply Check - Before allowing an object to reply to a transactional request, a check is made to ensure that the object has received replies to all its deferred synchronous requests that propagated the transaction in the original request. If this condition is not met, an exception is raised and the transaction is marked as rollback-only, that is, it cannot be successfully committed. A Transaction Service may check that a reply is issued within the context of the transaction associated with the request
  • Commit Check - Before allowing commit to proceed, a check is made to ensure that (1) The commit request for the transaction is being issued from the same execution environment that created the transaction. (2) The client issuing commit has received replies to all the deferred synchronous requests it made that caused the propagation of the transaction
  • Resume Check - Before allowing a client or object to associate a transaction context with its thread of control, a check is made to ensure that this transaction context was previously associated with the execution environment of the thread. This would be true if the thread either created the transaction or received it in a transactional operation
Typical Use Case

A typical use case will consist of a Transaction originator which is responsible for starting the transaction. Typically, the model chosen will be Indirect Context Management with Implicit propagation. This model will consist of the the transaction originator using the TransactionCurrent object to initiate the transaction by calling begin() operation. This will associate the current thread with a new transaction context.

The transaction originator will then call methods of a Transactional object. Because the model chosen is implict transaction context propagation, the context is automatically sent over the wire to the transactional object. The transactional object can access the transaction context by using the TransactionCurrent object. To preserve the transactional integrity, it may not actually do much with the transaction, but to suggest rolling back. If it tries to do anything else, the checked transaction behavior may be compromised. The transactional object itself may not have reliable and recoverable data store. If it did, then it would also be a recoverable object. Typically, such objects may call on external recoverable objects most likely which would be database objects. Databases can be integrated into the OTS model by adapting a resource object to participate in the OTS transaction and perform the XA operations on its behalf. A transactional object could call on many such recoverable objects.

The recoverable objects are responsible for maintaining the data by making sure that the data change is isolated and durable. The recoverable object participates in the transaction by registering a Resource object with the transaction. The recoverable object could call get_control() operation on the Transaction Current object to get access to the Control object for the transaction. Using this Control object it could further access the Coordinator object by using the get_coordinator() operation on the Control object. Using the Coordinator object, it can then register the Resource object. Please note that the recoverable object may recieve multiple invocations as part of a transaction. So, typically a new resource is NOT registered each time. has_transactioin() and is_same_transaction() operations can be used on the Control object to verify if it is the same transaction or a different transaction.

The resource object would then participate later in transaction completion. If there are multiple resource objects registered, then a 2-phase completion would be enacted otherwise one phase completion would do.

A transaction object could also register a Synchronization object with the transaction to get notifications before and after transaction completions.

Once the transaction is completed, the transaction originator commits or rolls back the transaction. At this stage by passing in true for the report_heuristics flag into commit, the originator can wait for the result of the transaction completion. If the flag is set to false, commit() returns immediately after the coordinator has made decision to commit or rollback. It does not wait for the 2-PC to complete.

Creating a Transactional CORBA object

With the original OMG OTS (1.0 and 1.1) specification, for making an an object transactional, its interface was required to inherit from the marker interface TransactionalObject. However, this scheme of things did not quite work out well, as the CORBA object's transactional behavior was hard-coded right at the design time. Another issue with this approach was that the specification provided a weak transactional semantics for the transactional object. There was no way to specify that the client always required to have a transactional context.

Another issue with this approach was that it always had the concept of the "shared transaction". Before the introduction of AMI - asynchronous messaging interface, the transaction model was end-to-end shared by the client and the server, as it was a single request. However, with the introduction of AMI, this model did not suffice as a request between a client and a server is split into multiple requests between the client, its intermediary routers and the server. This needed a new notion of unshared transactional model by which multiple shared transactions are used each completing before the next begins.

To support these requirements, OMG uses the POA policies to describe the transactional behavior.

OTSPolicy

This POA policy specifies the explicit transactional behavior. It can have three values
  • REQUIRES - The behavior of the target object depends on the existence of a current transaction. If the invocation does not have a current transaction, a TRANSACTION_REQUIRED exception will be raised.
  • FORBIDS - The behavior of the target object depends on the absence of a current transaction. If the invocation does have a current transaction, an INVALID_TRANSACTION exception will be raised.
  • ADAPTS - The behavior of the target object will be adjusted to take advantage of a current transaction, if one exists. If not, it will exhibit a different behavior (i.e., the target object is sensitive to the presence or absence of a current transaction).
InvocationPolicy

This supports the specification of whether shared or unshared model of transaction is needed. With the introduction of AMI, the old shared model will not work any more. So, depending on the kind of client, either shared or unshared needs to be chosen. The values of this policy are -
  • SHARED - All invocations which do not involve a routing element (i.e., the client ORB directly invokes the target object with no intermediate routers). This includes:(1) synchronous stub based invocations, (2) synchronous or deferred synchronous invocations using Dynamic Invocation Interface (DII)
  • UNSHARED - All invocations that involve a routing element. This includes Asynchronous Method Invocations (AMI) with an effective RoutingPolicy of ROUTE_FORWARD or ROUTE_STORE_AND_FORWARD.
s
Transaction Completion

Following are the steps that occur during transaction completion.
  • Before starting the completion, call before_completion on all the synchronization objects.
  • If all before_completion return successfully, then call prepare on each registered resource object.
  • If all resource prepare return VOTE_READONLY, then the transaction is considered completed and after_completion is called on each of the synchronization object and if client had asked for heuristics outcome, it is returned back.
  • If all resouce prepare return either VOTE_READONLY or VOTE_COMMIT, the transaction outcome is considered committed and commit is called on each resource that returned VOTE_COMMIT. After this, after_completion is called on each of the synchronization object and if the client had asked for heuristics outcome, it is returned back.
  • If any resource prepare returns VOTE_ROLLBACK, the transaction is considered rolledback and rollback is called on each of the previous resources which had returned VOTE_COMMIT. after_completion is then called on all the synchronization objects and the heuristics outcome is returned back to the client if asked for.
s