posts - 42,comments - 83,trackbacks - 0

        这几天做transaction recover测试的时候,发现个很难理解的问题。
        
        具体问题场景如下:
        1:一个standby client程序,调用userTransaction.begin()后,顺序操作两个XA resource,这两个resource为同一个Oracle database, 不过操作使用的connection来源于不同的data source。connection1向表test中插入一条数据,connection2向表test1中插入一条数据。最后执行userTransaction.commit()。
        2:userTransaction.commit()执行的时,需要执行两阶段提交,首先是global prepare,如果所有的resource都prepare ok的话,weblogic这时候会写入tx record(写入到tlog中)。然后执行global commit。测试过程中,在执行完global prepare后,在global commit处设定break point,然后停止database。数据库停止后,去除global commit的break point, 此时weblogic需要向每个resource发出commit指令。因为部分resource此时是unavailable的,weblogic无法收到每个resource commit完成的响应,该tx信息会一直保存在tlog中,等待server重起的时候recover。
        3:按照正常逻辑global prepare完成后,该tx应该是只能commit,而不能rollback的,但在测试中发现,weblogic在recover的时候 ,会去rollback这个tx。

        下面是测试中记录的数据信息。
         1:数据库重起后,weblogic重起前,可以看到database中该tx信息如下:

        2:weblogic tlog中的信息,
+------------------------------------------------------------------------------+
| Transaction Log Dump |                                                       |
+------------------------------------------------------------------------------+

| Class Name = weblogic.transaction.internal.ResourceCheckpoint                |

| Object = ResourceCheckpoint={OracelXADS, OracleXADS1}                        |

+------------------------------------------------------------------------------+


        3:weblogic重起后,可以看到该tx branch在recover的时候被rollback,
        ####<Nov 21, 2008 11:20:33 AM CST> <Debug> <debug@XAResourceDescriptor> <why> <AdminServer> <[STANDBY] ExecuteThread: '4' for queue: 'weblogic.kernel.Default (self-tuning)'> <<WLS Kernel>> <> <> <1227237633250> <000000> <to rollback tx, xid: BEA1-0000669563242B7A1CFC-4F726163656C58414453> 
        这个信息是我debug出来的,weblogic会依据xid(branchid),调用XAResource.rollback(Xid xid), 将该事务分支rollback。现在看到的问题是:4F7261636C655841445331分支已经commit了,我们调用XAResource.rollback(Xid xid), rollback 0000669563242B7A1CFC-4F726163656C58414453后,4F7261636C655841445331怎么也被rollback了呢? 

  Note: 在weblogic recover的时候,XAResource需要执行后端的存储过程,如果对应用户没有权限的话,weblogic server的日志里能看到如下信息:

####<Nov 20, 2008 7:12:24 PM CST> <Debug> <JDBCDriverLogging> <why> <AdminServer> <[STANDBY] ExecuteThread: '3' for queue: 'weblogic.kernel.Default (self-tuning)'> <<WLS Kernel>> <> <> <1227179544296> <000000> <SQLException: SQLState(65000) vendor code(6550)>
####<Nov 20, 2008 7:12:24 PM CST> <Debug> <JDBCDriverLogging> <why> <AdminServer> <[STANDBY] ExecuteThread: '1' for queue: 'weblogic.kernel.Default (self-tuning)'> <<WLS Kernel>> <> <> <1227179544296> <000000> <java.sql.SQLException: ORA-06550: line 1, column 7:
PLS-00201: identifier 'SYS.DBMS_SYSTEM' must be declared
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored

        碰到这种错误的时候,执行如下的SQL:grant execute on sys.dbms_system to system;

########################后续#####################

        经过半天的调查,终于找到问题的原因了。问题出在Oracle XA Driver上,我们使用来自于不同datasource的XAConnection对同一个database操作的时候,我们把它们当作两个事务分支,分支ID如下:
Branch 1: 4F7261636C655841445331
Branch 2: 4F7261636C65584144533
如果这两个事务分支分别指向不同的database的话,global prepare时,xaResource.prepare()的结果应该为OK(prepare的时候,database工作正常)。而我们上面的测试中,因为操作的是统一database,其prepare的结果分别是:OK, READ_ONLY(数据库端认为该TX branch对应的操作不涉及数据修改)。返回READ_ONLY的时候,因为database认为该 tx branch不涉及数据修改,所以直接将它commit了。这也就解释了上面的疑问,global commit没有执行的时候,怎么就有tx branch被commit了。

        下面再看看,为什么tlog中只能看到check point信息,而没有记录tx信息。在global prepare中,所有的参与该tx的resource的prepare结果返回后,weblogic需要根据vote结果决定进行global commit or global rollback。如果所有resource prepare都没问题的话,则将进行global commit。而在global commit之前,weblogic需要判断是否要将该tx信息记录到tlog中,而是记录tx的依据就是vote结果为0(OK)的resource个数是否大于1。大于1,则记入tlog,否则不做记录。我们上面的测试中,因为只涉及两个resource,而这两个resource的vote结果分别为:0(OK), 3(READ_ONLY),所以该tx信息没有被记录到tlog中。

        weblogic在recover的时候,首先检查tlog中的tx信息,如果存在tx,则读取tx信息,并将该tx放入到当前server的txMap中。然后调用xaResource.recover(),该方法的执行结果是,resource manager(如database, jms server)返回自己手里的pending transaction的xid list。 weblogic遍历该xid list,如果发现某个xid在当前txMap中,则忽略它(该tx将会被其他线程commit),那些不在txMap中的xid将会被rollback,通过xaResource.rollback(Xid xid)实现。在我们上面的测试中,因为tx没有被记录到tlog中,所以weblogic在recover的时候会将database返回的xid: 0000669563242B7A1CFC-4F726163656C58414453 rollback。由于Oracle Driver的特殊处理,两个connection上的两个tx branch被合并成了一个branch(另一个被它认为是个READ_ONLY),所以在rollback的时候,test, test1两表上的数据都被rollback了。

        在使用两个不同的database测试中,能够看到tlog中的tx信息,weblogic server重启后,也能看到tx的commit。tlog信息如下:


        最后再说一下weblogic执行global prepare时,resource的prepare顺序:
        1:remote xa resource
        2:local xa resource
        3:non-XA resource( LLR,对于Emulate 2PC的nonXA reosurce, 它的prepare结果直接就是OK,其实是个假prepare)
        所谓的remote, local不是只resource managed的位置,而是指resource对应的coordinator的位置。
posted on 2008-11-23 19:55 走走停停又三年 阅读(2324) 评论(0)  编辑  收藏 所属分类: Weblogic

只有注册用户登录后才能发表评论。


网站导航: