classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view


Bryan Jacobs

I am using ActiveMQ, and Derby in an XA Transaction.

I have been unable to find a use case in which Bitronix will invoke the
XAResource.isSameRM to do a TMJOIN.

It appears from the code that Bitronix should support this behavior.

I realize that this problem could be related to a bug in Derby,
ActiveMQ, and/or Bitronix.

In the Bitronix class XAResourceMananger.findXAResourceHolderState
there is a line of code:

            if (xaResourceHolderState.getXAResource() == xaResource)
                return xaResourceHolderState;

This seems to be the problem because this never evaluates to true.  In
my mind testing to see if the XAResource are referencing the same memory
location would be an implmentation detail of the Resource Mananger?????
At this point I would think you would simply invoke isSameRM()????? I
could be missing something.

I have to admit that when resources are delisted this method works just
fine....So perhaps I'm not correct.

Any help/clarification would be greatly appreciated.


To unsubscribe from this list, please visit:

TestCode.txt (6K) Download Attachment
Reply | Threaded
Open this post in threaded view


Ludovic Orban

Well, your question is very much about an internal implementation detail of BTM. It might of course vary from a transaction manager to another so don't expect to see other implementations to work the same way. It's also a long and maybe complex story so take a deep breathe before reading the following.

TMJOIN is about merging (joining in XA vocable) two transaction branches (ie: two pieces of a single XA transaction) together. When two branches can be joined or not is a resource (ie: JDBC or JMS) implementation detail while when join is actually performed is a transaction manager / XA pooling detail that relates to how enlistement / delistment is implemented.

The BTM pools basically support two enlistment policies: conservative and aggressive. This is configurable using the deferConnectionRelease of the PoolingDataSource or PoolingConnectionFactory classes.

When deferConnectionRelease is set to true (the default) the pool will use a XA resource-friendly enlistment policy (conservative). This works fine with all resources up to my knowledge. When it is set to false, the pool will use an aggressive enlistment policy. The aggressive policy might lead to better performance (by using a more sophisticated pooling algorithm) but requires the resource to fully support transactions interleaving. Most don't and the conservative policy works fine in all cases so deferConnectionRelease should be left to true unless you're absolutely sure your resource fully supports interleaved transactions.

This is a brief overview of the way BTM works regarding resource enlistment which is at the core of the problem.

Now, how does enlistment is actually performed by the conservative policy ? Take this pseudo-code snippet as a basis:

 1  tm.begin();
 3  c = ds.getConnection();
 4  s = c.prepareStatement("some sql here");
 5  s.execute();
 6  s.close();
 7  c.close();
 9  c = ds.getConnection();
10  s = c.prepareStatement("some other sql here");
11  s.execute();
12  s.close();
13  c.close();
15  tm.commit();

The connection pool calls XAResource.start(TMNOFLAGS) at line 4. This is the first time since the beginning of the transaction the resource is actually used so this the last possible moment to start the resource.

At line 7, the connection is marked as not usable anymore by the application but is not released yet to the connection pool. XAResource.end() could be called at that moment but the BTM pool does not. The resource stays in started mode but since no more work can be done on it anymore (the c object willl refuse calls as it has been marked as closed) this is not a problem.

At line 9, a new connection is requested from the pool. To make the best possible use of the size-limited connection pool, the one that was marked as not usable anymore at line 7 is marked back as in-use (what I call 'connection recycling') then returned to the calling code.

At line 13, the exact same thing happens as at line 7: the connection is marked as not usable anymore and is kept in started mode.

Finally, when line 15 executes the very first thing the transaction manager does is make sure all started XA resources are ended before proceeding to two-phase commit and finally releasing the connection for good to the pool.

Another working implementation would be to call XAResource.end() when the connection is closed then XAResource.start(TMJOIN) when the connection is recycled.

In practive, the BTM way of working allows for the best possible compatibility with XA resources and it also presents a nice optimization: each call to XAResource.start() or end() could require a round-trip to the actual resource. This is the case with Oracle for instance.

That does not mean TMJOIN is never used as there are some cases when end() must be called on the resource, the most important one being when the transaction is suspended.

In the previous pseudo-code, if in place of line 8 you add:


you will see that XAResource.end() is called by suspend() and XAResource.start(TMJOIN) by the prepareStatement() call on line 10. Usage of the TMJOIN flag is of course subject to the result of the isSameRM() call.

The other mode of operations makes more usage of the TMJOIN flag as the resources are started / ended as soon as possible. This is because the connection can be returned to the pool as soon as it is marked as closed, there is no need to wait until the end of the 2PC since transactions can be interleaved (ie: you can run more than one transaction per connection).

I hope this answers your question.