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 走走停停又三年 閱讀(2338) 評論(0)  編輯  收藏 所屬分類: Weblogic
          主站蜘蛛池模板: 商城县| 即墨市| 清水河县| 肥城市| 元朗区| 瑞昌市| 安阳市| 桦川县| 磴口县| 东阿县| 黄骅市| 恩施市| 丰城市| 韩城市| 北票市| 沭阳县| 黄山市| 阳山县| 奉节县| 台江县| 措美县| 航空| 隆子县| 湘潭市| 和平县| 郑州市| 安吉县| 丹东市| 宽甸| 清丰县| 静安区| 巧家县| 萍乡市| 吴旗县| 苍梧县| 芷江| 崇明县| 天门市| 柞水县| 托克托县| 辉南县|