In Summary, there are two types of transactions:
Atomic transactions, which take short time to execute (typically seconds). These transactions satisfy all ACID attributes, and thus need to acquire data locks on resource managers such as SQL Server. If all operations within a transaction scope succeed, then the transactions is committed automatically, else if any operation fails the entire transaction is rolled back. This is done via a transaction manager such as DTC.
Long-running transactions, which might span hours or days. These transactions must not acquire data locks because of severe performance implications of doing so. As such both committing or rolling back a transaction, must be done explicitly by code; this is due to the absence of a transaction manager which takes care of this in the case of atomic transactions.
In long-running Txs, rolling back is more commonly referred to as compensation. This is because, most of the times, in a long running Tx, you cannot undo a certain action, but you can compensate it with another one. For example, you cannot undo sending a message to a bank to perform a payment. But you can compensate this later, by sending a cancellation message.
BizTalk Server supports both types of transactions.
The scope shape is used to indicate a Tx, and has a Tx property which can be set to None, Long Running, or Atomic.
I will explain what do Atomic and Long Running Txs mean for BizTalk, but first lets just review the options you have for setting these values.
First thing you need to know, is that an Ox itself has a Transaction Type property, which is by default set to None.
- If you set the Transaction Type of the Ox to Atomic, then it cannot contain any other Txs. This way the entire Ox will be atomic, and will only persist at the end
- If you set the Transaction Type of the Ox to Long Running, then it can contain both Atomic and Long Running scopes.
The nesting rules within scope shapes are similar:
- An Atomic scope cannot contain any other transactional scope…meaning Atomic Txs cannot be nested.
- However, a Long Running scope, can contain both Long Running and Atomic scopes
Long Running Transactions
Now lets see what Long Running scopes mean for BizTalk. The best way to explains Txs, is to give examples; so consider this example.
Here I have a long running Tx, which contains three expressions.
- The first, sets a variable X to 1
- The second, causes an exception
- While the third, sets variable Y to 1
Now clearly, in this case, the shape that causes the exception will suspend the Ox and the next shape will never execute.
Now because this is a long running Tx, no automatic rollback occurs. So by the time the Ox is suspended X will be 1, while Y will still have its default value.
In case, you want to rollback your action, you can right click the scope and select “New Exception Handler”. In this exception handler block, you can then reset X back to its initial value:
Long Running vs. None
Now, the question is, in the previous example, is there any real value from using a Long Running scope? Couldn’t I have just set the Transaction Type to None?
The answer is yes, absolutely, in my previous example, I could have just set Transaction Type to None; because Exception handler blocks are also supported when Transaction Type is set to None:
So, the second question is: in the previous example, when I reset the value of X, was that what I referred to as compensation.
Well, the answer is no. At least not as compensation is intended to be used in BizTalk. What I did is called exception handling.
Next, I will explain Atomic transactions, then after that I will explain how compensation works in BizTalk Server.
Lets now consider the same example, however, this time using atomic Txs.
In this case, because the scope is Atomic, when the exception occurs, X is automatically rolled back. Now in this example, the Ox will suspend. Most probably this is not what you want, so you need to stop the Ox from suspending by handling the exception.
However, atomic scopes do not have exception handling blocks. So you can use nested transactions as follows:
Here, I wrapped the Atomic scope in a parent Long running scope. I then added an exception handler block for the parent scope. When the atomic scope raises the exception, the exception handler will execute and the Ox will continue normally.
Before really diving into compensations…lets first finish the basics.
Compensation in BizTalk is supported for both long running and atomic Txs. While – as I explained previously – using compensation with long running Txs is the more common approach because there is no support for automatic rollback; however, in some cases, you might want to compensate an already committed atomic transaction; compensation can be used for such scenarios.
Compensation can be added by right clicking a long running or atomic scope and selecting “New Compensation Block”. You then place whatever shapes your business demands inside this block:
Finally, a compensation block is called using the Compensate shape which can be placed inside an Exception Handler block.
The most important thing to keep in mind regarding compensation in BizTalk, is that it occurs only for committed Txs. While this might seem obvious, many tend to confuse this with exception handling I showed you before.
Lets again consider an example. Examine the following design for a long running Tx:
X is set to 1. Then an exception is raised…which as I showed you before…moves the control to the exception handling block. Previously I reset X inside the exception handler block. However, this time, I use the Compensate shape to call the Compensate Block of the Tx…and its in that block where I attempt to reset X.
So the question: do you think the compensation block will get invoked?
The answer is no.
The reason is that – as I said at the beginning – compensation as a concept is valid only for a committed Tx. In this case, an exception was raised inside the Tx, which means the Tx as a whole has failed. That is why, such cases are handled by exception handling.
Lets consider a more complicated example. Now Examine the following design:
This is a case of nested Txs, where a call to the compensation block of a Tx, will in turn call all compensation blocks of the nested Txs in the reverse order.
Here you can see I have an outer long running scope, which contains two other long running scopes. Each of the inner scopes has a compensation block. The first inner scope, causes an exception. And finally, the outer scope has an exception handler to catch the exception, and then uses the compensation shape to call the compensation block of the outer scope. Again remember, that a call to the compensation block of the outer scope, will in turn call the compensation blocks of the inner scopes…well, at least it should.
Now, the first shape of scope 1 is executed. And X is set to 1. Next, an exception is raised. The control moves to the exception handling block of the outer exception This block uses the compensate shape to call the compensation block of the outer scope
Now, lets think about this for a moment. Compensations are only fired for succeeded Txs. So in this design, is there any succeded Tx?
The answer is no. Simply because the exception was raised in scope 1, so scope 1 definitely is not succeeded. Also, once that happened, the control shifted to the exception handling block thus skipping scope 2 altogether. So in this case, no compensation will be fired, and the Ox will resume normally at the shape after the outer scope.
Now lets do one change:
I moved the shape that causes the exception to the second scope.
The flow will now go as follows: Shapes 1 and 2 of scope 1 are executed. Now shape 1 of scope 2 is executed. Here an exception happens. The exception handler of the outer scope catches the exception. It then calls the outer scope compensation block using the compensate shape
Now this time, the exception happened in scope 2, so scope 1 is a succeeded Tx. Because it is succeeded, it can be compensated. So the compensation block of scope 1 is called.
So as you can see, compensation – as a concept – means that you have a previously committed Tx, that now for any reason you want to compensate.
This example, simulates such case, where scope 1 was happily committed, however, something went wrong in scope 2, so the parent scope decided that scope 1 needs to be compensated to accommodate for the failure of scope 2.
One final thing I need to mention, is that by default, Atomic Txs are not propagated to and from BizTalk. So a BizTalk orchestration cannot participate in a transaction initiated by other components, and similarly, a component cannot participate in a Tx initiated by BizTalk.
For example, if you call a .NET component which uses .NET TransactionScope, from an Expression shape inside an atomic scope; and if the scope fails, the DB operation of the .NET component will not roll back.
So, when using atomic scopes, BizTalk will satisfy ACID attributes only to any changes made to variables and messages inside that scope; in other words the scope of the Tx is limited to the MessageBox.
If Tx propagation is needed, then the external assemblies must use COM+ objects derived from system.EnterpriseServices.ServicedComponents. In that case only, can the Ox Tx participate in the component Tx using DTC.
Atomic Transactions and Messaging
Armed with the knowledge about Txs and being (hopefully) expert in BizTalk messaging, the following notes should make sense:
- First, if you use a Send Shape inside an Atomic scope; then the message will be sent to the messagebox, but will not be available for subscribers until the scope commits. That is because if the message was actually allowed to be picked up by subscribers – say for example a send port – then there would be no way to rollback this action.
- This means, that you cannot have Send and Receive shapes connected to a Request-Response port inside an Atomic scope. Since a message is not actually sent out until after the scope commits, then there is no way it will get the response inside the scope.
- Similarly, you cannot have a Receive shape who is following on a correlation set that was initialized by a Send Shape inside the same Atomic scope.