Monday, May 12, 2014

SOA Suite 11g - Transaction(s) boundaries and Threads

As we added new/updated components I figured it'd be wise to post a little bit about transaction semantics in 11g - and how they can be used to leverage the underlying JTA infrastructure that is backing the execution of components.

a) Mediator:
On the inbound side, when mediator is invoked via binding.ws it will create an manage a new transaction, otherwise (e.g. if invoked through an adapter it will inherit the existing transaction)
In more generic terms:
If a transaction is present, Mediator participates in that existing transaction. If transaction is not present, Mediator starts the transaction
In case of sync (aka sequential) routing rules, they will be all executed in the same transaction, and if an error occurs, a rollback will be issued.
Note: in this case even a fault policy that is configured against a mediator fault will NOT trigger.
In case of async routing rules, each rule will be executed in a new transaction, and if errors occur, a rollback will happen, and a fault policy can be configured to react to it.
b) BPEL:
By default (as in previous releases) BPEL will create a new transaction on request basis - that is if one already exists, it will be suspended, and a new one created. Upon completion of the child, the master one resumes.
However, if the request is async (that is one-way) we either inherit the transaction for the insert into the dehydration store (that is dlv_message in this case) or if one exists enlist transparently into it. So we guarantee you zero message loss. Either the invocation message is in the dehydration store, to be processed, or otherwise the consumer is notified via a fault.
In 10.1.3 we had a couple of flags that one could set on the consuming process (that is the partnerlink) as well as on the providing one, to chain an execution into a single global transaction. Namely, on the consumer side, one had to set transaction=participate on the partnerlink binding in bpel.xml, and on the providing side transaction=participate in the <configurations> section of bpel.xml.
In 11g BPEL - we adopted a more j(2)ee alike concept - and one only needs to set the (new) transaction flag on the calee bpel component. This can be done in the composite editor's source by adding bpel.config.transaction into a bpel component - as shown below
  <component name="InternalWarehouseService">

    <implementation.bpel src="InternalWarehouseService.bpel"/>

    <property name="bpel.config.transaction" 

         many="false" type="xs:string">required | requiresNew</property>

  </component>


The two possible values are required, which makes BPEL inherit the tx that is already there, or if not creating a new one (required), or suspending one if exists, and ALWAYS create a new one (requiresNew).
What are the implications of setting the above (the default is requiresNew)?
Case one - BPELCaller calls BPELCallee (the latter has requiresNew set)
a) BPELCallee replies (that is uses <reply>) with a fault: BPELCallee transaction is saved, BPELCaller get's the fault and can catch it
b) BPELCallee throws (that is uses <throw>) a fault, that is NOT handled: BPELCallee tx get's rolled back, BPELCaller get's the fault and can catch it
c) BPELCallee replies back with a fault (FaultOne), and then throws a fault (FaultTwo): BPELCallee tx get's rolled back, and BPELCaller get's FaultOne
d) BPELCallee throws (that is uses <throw>) a bpelx:rollback fault: BPELCallee tx get's rolled back, BPELCaller get's a remote fault
Case two - BPELCaller calls BPELCallee (the latter has required set)
a) BPELCallee replies (that is uses <reply>) with a fault: BPELCaller get's the fault and can catch it, BPELCaller owns the transaction - hence if he catches it - tx is committed, if BPELCaller does not handle it - global rollback.
b) BPELCallee throws (that is uses <throw>) a fault BPELCaller get's the fault and can catch it
c) BPELCallee replies back with a fault (FaultOne), and then throws a fault (FaultTwo): BPELCaller get's FaultOne
d) BPELCallee throws (that is uses <throw>) a bpelx:rollback fault: BPELCaller tx get's rolled back, no way to catch it.
So with the above in place - one thing that I usually have people try when they are in my 11g courses is to create two processes (BPELMaster and BPELChild), both sync, each using the same db adapter reference that inserts the same record (and hence causes a PK violation). (And of course has set xADatasourceName)
So what happens in the default (that is without bpel.config.transaction property set). Once the fault occurs, and is not handled - the BPELChild will rollback. If the BPELMaster has a catch block his transaction will commit, so you end up with the record from the Master in the db.
If you don't catch the fault in the master as well - you get a second rollback - two different transactions though :-)
The second exercise is to set the bpel.config.transaction to required, and run the testcase again. If no fault handlers are in place - the whole tx will rollback, based on the BPELMaster's unhandled fault.
Now add a fault handler in the BPELMaster to catch the Fault from the BPELClient - and throw up a rollback fault. Globally the tx will rollback as well.
With this in place - you can really control transaction boundaries and model end2end transactional flows (if your source / targets are transactional as well of course)

Here is the second post - this time on one-way invocations:
Usually a one way invocation (with a possible callback) is exposed in a wsdl as below
    <wsdl:operation name="process">

        <wsdl:input message="client:OrderProcessorRequestMessage"/>

    </wsdl:operation>

This will cause the bpel engine to split the execution into two parts. First, and always inside the caller transaction, the insert into the dlv_message table (in 10.1.3.x that is into the inv_message), and secondly the transaction & new thread that executes the workitems, and creates a new instance.
This has several advantages in terms of scalability - because the engine's threadpool (invoker threads) will execute when a thread is available. However, the disadvantage is that there is no guarantee that it will execute immediately.
If one needs to have a sync-type call based on a one way operation - then they can use onewayDeliveryPolicy, which is a forward port of deliveryPersistPolicy in 10.1.3.
This property can be set by specifying bpel.config.oneWayDeliveryPolicy in a bpel component of composite.xml. Possible values are "async.persist", "async.cache" and "sync". If this value is not set in composite.xml, engine uses the oneWayDeliveryPolicy setting in bpel-config.xml
async.persist => persist in the db
async.cache => store in an in-memory hashmap
sync => direct invocation on the same thread
Below is the matrix based on the usecase.
onewayDeliveryPolicy!=sync (default, callee runs in separate thread/treansation)
throw any fault
caller doesn't get response because message is saved in delivery service. The callee's transaction will rollback if the fault is not handled.
throw bpelx:rollback
caller doesn't get response because message is saved in delivery service. It will rollback on unhandled fault.
-------
onewayDeliveryPolicy=sync, txn=requriesNew (callee runs in the same thread, but different transaction)
throw any fault
caller gets FabricInvocationException. Callee transaction rolls back if the fault is not handled.
throw bpelx:rollback
caller gets FabricInvocationException. Callee transaction rolls back.
-------
onewayDeliveryPolicy=sync, txn=required (callee runs in the same thread and the same transaction)
throw any fault
Callee faulted. Caller gets FabricInvocationException. Caller has a chance to handle the fault.
throw bpelx:rollback
whole transaction rollback.

No comments:

Post a Comment