在分散式交易(JTA)測試中,發現一個反直覺現象:Global Prepare 成功、Global Commit 尚未執行時資料庫掛掉,WebLogic 重啟恢復時卻對已 Commit 的分支執行 Rollback,導致資料不一致。本文記錄完整複現步驟、日誌證據、根因分析(Oracle XA Driver READ_ONLY 行為)與 WebLogic Transaction Log (TLog) 記錄邏輯。
UserTransaction.begin()。UserTransaction.commit() 進入 Two-Phase Commit (2PC):資料庫端顯示交易分支狀態:Branch 1 (4F7261636C655841445331) 已 Commit,Branch 2 (4F7261636C65584144533) 狀態不明。
Transaction Log Dump
Class Name = weblogic.transaction.internal.ResourceCheckpoint
Object = ResourceCheckpoint={OracelXADS, OracleXADS1}
僅有 ResourceCheckpoint,無完整交易記錄。
<Debug> <XAResourceDescriptor> ... to rollback tx, xid: BEA1-0000669563242B7A1CFC-4F726163656C58414453
WebLogic 對 xid 結尾為 4F7261636C6558414453 的分支發起 XAResource.rollback(),導致原本已 Commit 的 Branch 1 (..31) 也被回滾。
OK (0),第二個 Resource 判定「無資料異動」回傳 READ_ONLY (3)。Global Prepare 結束後,WebLogic 統計 vote == OK (0) 的 Resource 數量:
count(OK) > 1 → 寫入完整 TLog 記錄(含交易狀態、參與者等)。count(OK) ≤ 1 → 僅寫入 ResourceCheckpoint,不寫入交易記錄。本案例:OK=1, READ_ONLY=1 → count(OK)=1 → 無完整 TLog。
txMap 為空。XAResource.recover() 取得資料庫端 Pending XID 列表。txMap → 判定為孤兒交易 → 呼叫 XAResource.rollback(Xid)。改用兩個不同 Oracle Database 實例測試:
Global Prepare → 兩 Resource 均回傳 OK → count(OK)=2 > 1 → 寫入完整 TLog → 重啟後正常 Commit。
TLog 截圖顯示完整交易資訊,恢復成功。
此處「Remote/Local」指 Collaborator 位置,非 Resource 管理位置。
SELECT 1 FROM DUAL 等無實質異動但能避免 READ_ONLY 的 SQL(需測試 Driver 行為)。ORA-06550: PLS-00201: identifier 'SYS.DBMS_SYSTEM' must be declared,需執行 GRANT EXECUTE ON SYS.DBMS_SYSTEM TO <user>;。此問題本質上是「Oracle XA Driver READ_ONLY 最佳化」與「WebLogic TLog 記錄門檻」交互導致。理解 2PC 中 READ_ONLY 語意(自行 Commit、不參與 Global Commit)與 WebLogic 以 OK 票數 > 1 決定是否落盤 TLog 的邏輯,才能在架構設計時規避此類資料一致性陷阱。