Re: XA end then join fix for WebLogic - Mailing list pgsql-jdbc
From | ludovic orban |
---|---|
Subject | Re: XA end then join fix for WebLogic |
Date | |
Msg-id | c016d00b0611010202m3438da13t4035164c822f1b78@mail.gmail.com Whole thread Raw |
In response to | XA end then join fix for WebLogic (Heikki Linnakangas <heikki@enterprisedb.com>) |
List | pgsql-jdbc |
Heikki, You wondered why Weblogic calls XAResource.start(TMJOIN) in this post: http://archives.postgresql.org/pgsql-jdbc/2006-10/msg00011.php I'm going to try to explain you why that's happening in this email. Please note that this explanation is solely based on my personal observations of multiple transaction managers/app servers. I'm writing this from the top of my head so I may have done a few mistakes. *** deep breathe, this is long and complex *** The transaction manager is not the masterpiece of this puzzle, the XADataSource wrapper (that usually provides pooling and exposes a (non-XA) DataSource interface) is. The way resources are enlisted/delisted depends on a chosen enlistment policy. Unfortunately the JTA spec leaves much to desire on this subject: there is not a single line describing how enlistment should be performed nor any kind of recommendation. Implementors are left on their own to choose an implementation and you basically have two major enlistment policies: on statement creation and on connection acquirement. Websphere and Atomikos TransactionsEssentials implement the second policy. Weblogic and Bitronix TM implement the first one. The biggest advantage of the 'on connection acquirement' one is that it's much (much, much, much !) easier to implement and works plain fine 9 times out of 10. This one calls XAResource.start(TMNOFLAGS) when XADataSource.getConnection() is called and XAResource.end(TMSUCCESS) when TransactionManager.commit() is called. The 'on statement creation' one, while more complex to write is more flexible (end-user wise) and is generally more performant when used with databases fully supporting transaction interleaving. This one calls XAResource.start(TMNOFLAGS) when Connection.createStatement() or prepareStatement() or prepareCall() is called and XAResource.end(TMSUCCESS) when Connection.close() is called. For a good example of the increased flexibility you get with the 'on statement creation' policy has been discussed by Dmitri Maximovich: http://www.jroller.com/page/maximdim?entry=is_xa_transaction_support_broken Basically only implementations using 'on statement creation' enlistment policy can support this special case. Now what would be the difference between the two in terms of XAResource calls ? Let's assume we have this small example: 1 public void transactionalMethod() { 2 tm.begin(); 3 4 executeSQL(); 5 executeSQL(); 6 7 tm.commit(); 8 } 9 10 private void executeSQL() { 11 Connection c = getXADataSource().getConnection(); 12 c.createStatement().executeUpdate("some SQL"); 13 c.close(); 14 } With 'on connection acquirement': line 11 (called by line 4): get a physical connection out of the connection pool and call XAResource.start(TMNOFLAGS). line 13 (called by line 4): mark the connection as 'not accessible' and keep it out of the connection pool. line 11 (called by line 5): get back the connection previously maked as 'not accessible'. line 13 (called by line 5): mark again the connection as 'not accessible' but don't do anything else. line 7: call XAResource.end(TMSUCCESS) on the connection's XAResource. Requeue the connection into the pool. How can line 11 (called by 5) get back the same connection that was previously acquired ? Simply by looking at all the connections marked as 'not accessible' and picking the one that has called XAResource.start() with a XID that has the same GTRID as the current global transaction's GTRID. If it can't find any this just means a new connection should be acquired from the pool but in this example that will never happen. With 'on statement creation': line 11 (called by line 4): nothing happens enlistment/delistment wise. line 12 (called by line 4): XAResource.start(TMNOFLAGS) is called. line 13 (called by line 4): XAResource.end(TMSUCCESS) is called and the connection is requeued. line 11 (called by line 5): nothing happens enlistment/delistment wise. line 12 (called by line 5): XAResource.start(TMJOIN) is called. line 13 (called by line 5): XAResource.end(TMSUCCESS) is called and the connection is requeued. line 7: nothing happens enlistment/delistment wise. How can line 12 (called by 5) knows it should call XAResource.start() with TMJOIN instead of TMNOFLAGS ? The TM must keep a list of resources enlisted in the current transaction (-> XAConnections which XAResource.start() has been called) and call XAResource.isSameRm(currentXAResource) on all of them with currentXAResource being the XAResource of the Connection acquired on line 11 (called by 5). If isSameRm() returns true, XAResource.start(TMJOIN) can be called but if it returns false XAResource.start(TMNOFLAGS) should be called instead. TMJOIN is much more performant as the database does not have to create a new transaction branch and isolate work done on both connections: it just works like if you called both executeUpdate() in the same local transaction while with TMNOFLAGS the database has to create a new branch and apply ACID semantics between the two executeUpdate() calls since it has to treat the two branches as two unrelated local transactions. Of course, subtle variations can happen depending on a lot of implementation-specific choices and potential configuration parameters but this gives you the basic idea of what's happening under the cover. Cheers, Ludovic
pgsql-jdbc by date: