這幾天做transaction recover測試的時候,發(fā)現(xiàn)個很難理解的問題。
具體問題場景如下:
1:一個standby client程序,調(diào)用userTransaction.begin()后,順序操作兩個XA resource,這兩個resource為同一個Oracle database, 不過操作使用的connection來源于不同的data source。connection1向表test中插入一條數(shù)據(jù),connection2向表test1中插入一條數(shù)據(jù)。最后執(zhí)行userTransaction.commit()。
2:userTransaction.commit()執(zhí)行的時,需要執(zhí)行兩階段提交,首先是global prepare,如果所有的resource都prepare ok的話,weblogic這時候會寫入tx record(寫入到tlog中)。然后執(zhí)行g(shù)lobal commit。測試過程中,在執(zhí)行完global prepare后,在global commit處設(shè)定break point,然后停止database。數(shù)據(jù)庫停止后,去除global commit的break point, 此時weblogic需要向每個resource發(fā)出commit指令。因為部分resource此時是unavailable的,weblogic無法收到每個resource commit完成的響應(yīng),該tx信息會一直保存在tlog中,等待server重起的時候recover。
3:按照正常邏輯global prepare完成后,該tx應(yīng)該是只能commit,而不能rollback的,但在測試中發(fā)現(xiàn),weblogic在recover的時候 ,會去rollback這個tx。
下面是測試中記錄的數(shù)據(jù)信息。
1:數(shù)據(jù)庫重起后,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會依據(jù)xid(branchid),調(diào)用XAResource.rollback(Xid xid), 將該事務(wù)分支rollback。現(xiàn)在看到的問題是:4F7261636C655841445331分支已經(jīng)commit了,我們調(diào)用XAResource.rollback(Xid xid), rollback 0000669563242B7A1CFC-4F726163656C58414453后,4F7261636C655841445331怎么也被rollback了呢?
Note: 在weblogic recover的時候,XAResource需要執(zhí)行后端的存儲過程,如果對應(yīng)用戶沒有權(quán)限的話,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
碰到這種錯誤的時候,執(zhí)行如下的SQL:grant execute on sys.dbms_system to system;
########################后續(xù)#####################
經(jīng)過半天的調(diào)查,終于找到問題的原因了。問題出在Oracle XA Driver上,我們使用來自于不同datasource的XAConnection對同一個database操作的時候,我們把它們當(dāng)作兩個事務(wù)分支,分支ID如下:
Branch 1: 4F7261636C655841445331
Branch 2: 4F7261636C65584144533
如果這兩個事務(wù)分支分別指向不同的database的話,global prepare時,xaResource.prepare()的結(jié)果應(yīng)該為OK(prepare的時候,database工作正常)。而我們上面的測試中,因為操作的是統(tǒng)一database,其prepare的結(jié)果分別是:OK, READ_ONLY(數(shù)據(jù)庫端認(rèn)為該TX branch對應(yīng)的操作不涉及數(shù)據(jù)修改)。返回READ_ONLY的時候,因為database認(rèn)為該 tx branch不涉及數(shù)據(jù)修改,所以直接將它c(diǎn)ommit了。這也就解釋了上面的疑問,global commit沒有執(zhí)行的時候,怎么就有tx branch被commit了。
下面再看看,為什么tlog中只能看到check point信息,而沒有記錄tx信息。在global prepare中,所有的參與該tx的resource的prepare結(jié)果返回后,weblogic需要根據(jù)vote結(jié)果決定進(jìn)行g(shù)lobal commit or global rollback。如果所有resource prepare都沒問題的話,則將進(jìn)行g(shù)lobal commit。而在global commit之前,weblogic需要判斷是否要將該tx信息記錄到tlog中,而是記錄tx的依據(jù)就是vote結(jié)果為0(OK)的resource個數(shù)是否大于1。大于1,則記入tlog,否則不做記錄。我們上面的測試中,因為只涉及兩個resource,而這兩個resource的vote結(jié)果分別為:0(OK), 3(READ_ONLY),所以該tx信息沒有被記錄到tlog中。
weblogic在recover的時候,首先檢查tlog中的tx信息,如果存在tx,則讀取tx信息,并將該tx放入到當(dāng)前server的txMap中。然后調(diào)用xaResource.recover(),該方法的執(zhí)行結(jié)果是,resource manager(如database, jms server)返回自己手里的pending transaction的xid list。 weblogic遍歷該xid list,如果發(fā)現(xiàn)某個xid在當(dāng)前txMap中,則忽略它(該tx將會被其他線程commit),那些不在txMap中的xid將會被rollback,通過xaResource.rollback(Xid xid)實現(xiàn)。在我們上面的測試中,因為tx沒有被記錄到tlog中,所以weblogic在recover的時候會將database返回的xid: 0000669563242B7A1CFC-4F726163656C58414453 rollback。由于Oracle Driver的特殊處理,兩個connection上的兩個tx branch被合并成了一個branch(另一個被它認(rèn)為是個READ_ONLY),所以在rollback的時候,test, test1兩表上的數(shù)據(jù)都被rollback了。
在使用兩個不同的database測試中,能夠看到tlog中的tx信息,weblogic server重啟后,也能看到tx的commit。tlog信息如下:
最后再說一下weblogic執(zhí)行g(shù)lobal prepare時,resource的prepare順序:
1:remote xa resource
2:local xa resource
3:non-XA resource( LLR,對于Emulate 2PC的nonXA reosurce, 它的prepare結(jié)果直接就是OK,其實是個假prepare)
所謂的remote, local不是只resource managed的位置,而是指resource對應(yīng)的coordinator的位置。
posted on 2008-11-23 19:55
走走停停又三年 閱讀(2332)
評論(0) 編輯 收藏 所屬分類:
Weblogic