Overview and Key Facts
- JTA uses javax.transaction.UserTransaction API to manage transaction bounderies
- Each UserTransaction is associated with a Thread and this thread can only handle 1 active Transaction in parallel
- If a UserTransaction times out the Transaction will be rolled back and Transaction status will be set to : STATUS_ROLLING_BACK
- To manage Transaction Timout use following API: ut.setTransactionTimeout(txTimeout)
- In this sample 2 Wildfly we have configured RAC XA datasources :
- RAC bankA maintained and running in London
- RAC bankB maintained and running in Frankfurt
- A typical XA transaction consists out of at least 3 different processing steps
- Step 1: Data processing : xa_start DML operation xa_end
- Step 2: Prepare the transaction: xa_prepare
- Step 3: Commit the transaction: xa_commit
- Step 2 and 3 are called 2-Phase-Commit or short 2PC
- If clusterwide XA transaction are enabled any RAC instance can run any of the above steps
- 2PC Commit Protocol details:
- Prepare phase: The TM asks participating RMs to promise to commit (or rollback) the transaction as requested, even if there is a failure. If any RM cannot prepare, the transaction is rolled back.
- Commit phase: If all RMs respond to the TM that they are prepared, then the TM asks all RMs commit the transaction.
- ut.begin() starts the XA transaction whereas ut.commit() ends, prepares and commits the XA transaction
- DBA_PENDING_TRANSACTIONS describes unresolved transactions (either due to failure or if the coordinator has not sent a commit/rollback).
- DBA_2PC_PENDING describes in-doubt transactions ( after tx timeout the RAC database records these XA transactions missing a commit/rollback)
- A global XA transaction can span Oracle RAC instances by default ( if not using DTP services ) –
-
The GLOBAL_TXN_PROCESSES initialization parameter, which is set to 1 by default, specifies the initial number of GTX n background processes for each Oracle RAC instance.
-
If GLOBAL_TXN_PROCESSES > 1 clusterwide XA transactions are enabled.
Configuration and Setup
- Test Framework – click on the image below to find out details !
JDK Version: 1.7.0_71
JDBC-Driver: Oracle JDBC driver - Version: 12.1.0.2.0
DB Name: BANKA
Running Instances:
10:06:16.646 Instance Name: bankA_1 - Host: hract22.example.com
10:06:16.647 Instance Name: bankA_3 - Host: hract21.example.com
DB Name: BANKB
Running Instances:
10:06:17.029 Instance Name: bankb_3 - Host: hract22.example.com
10:06:17.029 Instance Name: bankb_1 - Host: hract21.example.com
In this tutorial we will test and evaluate a 2-Phase-Commit scenario between:
RAC database bankA and RAC database bankB
RAC db version: 12.1.0.2 / Wildfyl 8.0.2
To run this code you need to create 2 Oracle XA datasources named
java:jboss/datasources/xa_rac12g_banka
java:jboss/datasources/xa_rac12g_bankb
For details creating a XA datasource with Oracle RAC please read following article:
Setup a XA Datasource by using the RAC SCAN address
Java Code to run the 2-Phase-Commit operation
@Named("xaTestBean")
@SessionScoped
public class XaTestBean implements Serializable
{
@Resource(lookup="java:jboss/datasources/xa_rac12g_banka")
private DataSource ds1;
@Resource(lookup="java:jboss/datasources/xa_rac12g_bankb")
private DataSource ds2;
@Resource
private UserTransaction ut;
..
public String transferMoney()
{
initTopLevel();
setTxInfo(Tools.add_hmtl_pre_tag("Calling transferMoney() in progress - try getting XA connections !" )) ;
try
{
try ( Connection c1 = ds1.getConnection();
Connection c2 = ds2.getConnection(); )
{
setSess1(Tools.add_hmtl_pre_tag(c1.getMetaData().getURL()));
setSess2(Tools.add_hmtl_pre_tag(c2.getMetaData().getURL()));
setSess1(getSess1() + Tools.add_hmtl_pre_tag(getSessionInfo(c1)));
System.out.println(getSess1()); System.out.flush();
setSess2(getSess2() + Tools.add_hmtl_pre_tag( getSessionInfo(c2)));
System.out.println(getSess2() ); System.out.flush();
setTxInfo(Tools.add_hmtl_pre_tag("No TX active - before ut.begin() " + returnUtStatus(ut) + " - Transaction Timeout: " + txTimeout )) ;
ut.setTransactionTimeout(txTimeout) ;
setTxInfo(getTxInfo() + Tools.add_hmtl_pre_tag("Setting New Tansaction Timeout : " + txTimeout));
ut.begin();
setTxInfo(getTxInfo() + Tools.add_hmtl_pre_tag("After ut.begin() " + returnUtStatus(ut) )) ;
doUpdate(c1, " - Session 1", "update Accounts set balance = balance - 4000");
doUpdate(c2, " - Session 2", "update Accounts set balance = balance + 4000");
setTxInfo(getTxInfo() + Tools.add_hmtl_pre_tag("Tx status before ut.commit() - " + returnUtStatus(ut) + " - Now sleeping : "
+ sleepTime + " [ seconds] " )) ;
Thread.sleep(1000*sleepTime);
ut.commit();
setTxInfo(getTxInfo() + Tools.add_hmtl_pre_tag("Tx status after ut.commit() " + returnUtStatus(ut) )) ;
// Update Status in index.html too
setBankOperation(getBankOperation() + Tools.add_hmtl_pre_tag(getAccountinfo(c1)));
setBankOperation(getBankOperation() + Tools.add_hmtl_pre_tag(getAccountinfo(c2)));
setBankOperation(getBankOperation() + Tools.add_hmtl_pre_tag("Leaving transferMoney() without any errors"));
c1.close();
c2.close();
setTxInfo(getTxInfo() + Tools.add_hmtl_pre_tag("Leaving transferMoney() without any errors"));
}
} catch ( Exception ex )
{
String e = ex.getMessage();
genericExp("Error in JSF top level function: transferMoney() - Need to ROLLBACK transaction", ex);
setBankOperation(getBankOperation() + Tools.add_hmtl_pre_tag("FATAL ERROR in transferMoney(): " + e ));
try
{
setTxInfo(getTxInfo() + Tools.add_hmtl_pre_tag("FATAL ERROR: "));
setTxInfo(getTxInfo() + Tools.add_hmtl_pre_tag( "Tx status after Exception before ut.rollback(): " +
returnUtStatus(ut) + " - Exception: "+ e));
ut.rollback();
setTxInfo(getTxInfo() + Tools.add_hmtl_pre_tag("Tx status after Exception - but sucessful ut.rollback(): " + returnUtStatus(ut) ));
} catch( Exception e2 )
{
setTxInfo(getTxInfo() +Tools.add_hmtl_pre_tag( "Tx status after Exception: FATAL error during ut.rollback() :" +
returnUtStatus(ut) + " - Exception: " +e2));
}
return "bankInfo";
}
return "bankInfo";
}
Testing SQL Exceptions in function transferMoney():
Prepared Test case:
Press :
Press Transfer Money 3x :
Output from index.xhtml:
09:59:54.830 Calling checkAccounts() in progress ...
09:59:54.835 Database Name:BANKA -- Account: User99_at_BANKA -- Balance: 10000.0
09:59:54.839 Database Name:BANKB -- Account: User98_at_BANKB -- Balance: 10000.0
09:59:54.839 Leaving checkAccounts() without any Error
10:00:12.397 Database Name:BANKA -- Account: User99_at_BANKA -- Balance: 6000.0
10:00:12.405 Database Name:BANKB -- Account: User98_at_BANKB -- Balance: 14000.0
10:00:12.405 Leaving transferMoney() without any errors
10:00:18.232 Database Name:BANKA -- Account: User99_at_BANKA -- Balance: 2000.0
10:00:18.248 Database Name:BANKB -- Account: User98_at_BANKB -- Balance: 18000.0
10:00:18.249 Leaving transferMoney() without any errors
10:00:21.376 FATAL ERROR in transferMoney(): ORA-02290: check constraint (SCOTT.S_LOWER_CHK) violated
Last SQL:
10:00:21.373 update Accounts set balance = balance - 4000 - Session 1
Last SQL Error:
10:00:21.373 Exception in Class: com.hhu.XaTestBean - Methode: doUpdate
java.sql.SQLIntegrityConstraintViolationException: ORA-02290: check constraint (SCOTT.S_LOWER_CHK) violated
Url: jdbc:oracle:thin:@ract2-scan.grid12c.example.com:1521/banka User: SCOTT
Stmt: update Accounts set balance = balance - 4000 - Session 1
TX Info:
10:00:21.267 No TX active - before ut.begin() TX status: 6 - STATUS_NO_TRANSACTION - Transaction Timeout: 5
10:00:21.267 Setting New Tansaction Timeout : 5
10:00:21.273 After ut.begin() TX status: 0 - STATUS_ACTIVE
10:00:21.377 FATAL ERROR:
10:00:21.377 Tx status after Exception before ut.rollback(): TX status: 0 - STATUS_ACTIVE
- Exception: ORA-02290: check constraint (SCOTT.S_LOWER_CHK) violated
10:00:21.383 Tx status after Exception - but sucessful ut.rollback(): TX status: 6 - STATUS_NO_TRANSACTION
Code Explanation
- After calling transferMoney() a 3rd time the database check contraint gets fired as our balance moves to a negative value
- Inside the exeception handler the XA transaction was successfully rolled back and our thread ends up with setting its XA status to : STATUS_NO_TRANSACTION
Testing automatic transaction recovery after a COMMIT record was written to Wildlfly LOG-STORE
- Both XA resources have voted to COMMIT the transaction
- Wildfly has written a COMMIT record to the Wildlfly LOG-STORE
- After this all transaction branches needs to be committed [ either by application server or by the DB admin ]
- After a system crash Periodic Recovery Thread should use
XA recover and XA commit to commit this transaction without any user intervention
Setup Testcase :
Press Init Accounts :
11:04:21.039 Calling checkAccounts() in progress ...
11:04:21.088 Database Name:BANKA -- Account: User99_at_BANKA -- Balance: 10000.0
11:04:21.128 Database Name:BANKB -- Account: User98_at_BANKB -- Balance: 10000.0
11:04:21.128 Leaving checkAccounts() without any Error
Breakpoints : Class oracle.jdbc.xa.OracleXAResource : Methode entry for prepare() and commit()
Start application by pressing :
Use debugger and stop processing at Entry to first OracleXAResource.commit call [ Press F5 until you reach this code point ]
Method breakpoint hit in oracle.jdbc.xa.client.OracleXAResource.prepare at line 693 by thread default task-6.
Method breakpoint hit in oracle.jdbc.xa.client.OracleXAResource.prepare at line 693 by thread default task-6.
Method breakpoint hit in oracle.jdbc.xa.client.OracleXAResource.commit at line 553 by thread default task-6.
-> Note: Wildfly has already written this transaction to its log
Verify Wildfly LOG-STORE
[oracle@wls1 XA]$ $WILDFLY_HOME/bin/jboss-cli.sh --connect --file=xa.cli
{"outcome" => "success"}
0:ffffc0a805c9:-46761d77:55438636:7ee
-> Wildfly has saved the transaction to its LOG-STORE
Check transaction status at Database level
SQL> select host_name,instance_name from v$instance;
HOST_NAME INSTANCE_NAME
------------------------------ ----------------
hract22.example.com bankA_1
SQL> select STATE, LOCAL_TRAN_ID, GLOBAL_TRAN_ID , to_char(FAIL_TIME,'DD-MON-YY HH24:MI:SS') FAIL_TIME,
2 to_char(FORCE_TIME,'DD-MON-YY HH24:MI:SS') FORCE_TIME, to_char(RETRY_TIME,'DD-MON-YY HH24:MI:SS') RETRY_TIME,
3 HOST, OS_USER, COMMIT# from dba_2pc_pending;
STATE LOCAL_TRAN_ID GLOBAL_TRAN_ID
---------------- ---------------------- --------------------------------------------------------------------------------
FAIL_TIME FORCE_TIME RETRY_TIME HOST OS_USER COMMIT#
------------------ ------------------ ------------------ ------------------------- --------------- ----------------
prepared 7.6.4154 131077.00000000000000000000FFFFC0A805C9B989E28955438636000007EE31
03-MAY-15 08:44:32 03-MAY-15 08:45:05 wls1.example.com oracle 24656988
SQL> select host_name,instance_name from v$instance;
HOST_NAME INSTANCE_NAME
------------------------------ ----------------
hract22.example.com bankb_3
SQL> select STATE, LOCAL_TRAN_ID, GLOBAL_TRAN_ID , to_char(FAIL_TIME,'DD-MON-YY HH24:MI:SS') FAIL_TIME,
2 to_char(FORCE_TIME,'DD-MON-YY HH24:MI:SS') FORCE_TIME, to_char(RETRY_TIME,'DD-MON-YY HH24:MI:SS') RETRY_TIME,
3 HOST, OS_USER, COMMIT# from dba_2pc_pending;
STATE LOCAL_TRAN_ID GLOBAL_TRAN_ID
---------------- ---------------------- --------------------------------------------------------------------------------
FAIL_TIME FORCE_TIME RETRY_TIME HOST OS_USER COMMIT#
------------------ ------------------ ------------------ ------------------------- --------------- ----------------
prepared 16.17.405 131077.00000000000000000000FFFFC0A805C9B989E28955438636000007EE31
03-MAY-15 08:44:29 03-MAY-15 08:45:20 wls1.example.com oracle 9841282
**********************************************************
*** -> kill java process/program and restart Wildlfy ****
**********************************************************
[oracle@wls1 XA]$ ps -elf | grep java
0 S oracle 9264 9210 19 80 0 - 414974 futex_ May02 ? 01:44:51 /usr/java/latest/bin/java -D[Standalone] -Xms128m -Xmx512m -XX:MaxPermSize=256m -server ..
[oracle@wls1 XA]$ kill -9 9264
-> Restart Wildfly - Wildfly Server Log reports
2015-05-03 08:50:45,017 WARN [com.arjuna.ats.jta] (Periodic Recovery) ARJUNA016037:
Could not find new XAResource to use for recovering non-serializable XAResource XAResourceRecord
< resource:null, txid:< formatId=131077, gtrid_length=29, bqual_length=36,
tx_uid=0:ffffc0a805c9:-46761d77:55438636:7ee, node_name=1,
branch_uid=0:ffffc0a805c9:-46761d77:55438636:7f0, subordinatenodename=null,
eis_name=java:jboss/datasources/xa_rac12g_bankb >,
heuristic: TwoPhaseOutcome.FINISH_OK, product:
Oracle/Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production
With the Partitioning, Real Application Clusters, Automatic Storage Management, OLAP,
Advanced Analytics and Real Application Testing options,
jndiName: java:jboss/datasources/xa_rac12g_bankb com.arjuna.ats.internal.jta.resources.arjunacore.XAResourceRecord@519e22cc >
2015-05-03 08:50:45,017 WARN [com.arjuna.ats.jta] (Periodic Recovery) ARJUNA016037:
Could not find new XAResource to use for recovering non-serializable XAResource XAResourceRecord
< resource:null, txid:< formatId=131077, gtrid_length=29, bqual_length=36,
tx_uid=0:ffffc0a805c9:-46761d77:55438636:7ee, node_name=1,
branch_uid=0:ffffc0a805c9:-46761d77:55438636:7f3, subordinatenodename=null,
eis_name=java:jboss/datasources/xa_rac12g_banka >,
heuristic: TwoPhaseOutcome.FINISH_OK,
product: Oracle/Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production
With the Partitioning, Real Application Clusters, Automatic Storage Management, OLAP,
Advanced Analytics and Real Application Testing options,
jndiName: java:jboss/datasources/xa_rac12g_banka com.arjuna.ats.internal.jta.resources.arjunacore.XAResourceRecord@64fea8f0 >
-> Periodic Recovery Thread successfully re-connects to both databases
Periodic Recovery Thread calls XA recover to retrieve the prepared XIDs from the server
Periodic Recovery Thread finally commits the data as Wildfly LOG-STORE confirms that all branches are prepared
Verify that that the data was really committed
GLOBAL_NAME
--------------------------------------------------------------------------------
BANKA
ACCOUNT BALANCE
-------------------------------- ----------
User99_at_BANKA 6000
GLOBAL_NAME
--------------------------------------------------------------------------------
BANKB
ACCOUNT BALANCE
-------------------------------- ----------
User98_at_BANKB 14000
Verify Wildfly log :
[oracle@wls1 XA]$ $WILDFLY_HOME/bin/jboss-cli.sh --connect --file=xa.cli
{"outcome" => "success"}
-> After Wildfly restart the transaction was committed and the Wildfly LOG-Store was cleaned !
Testing automatic transaction recovery before a COMMIT record was written to Wildlfly LOG-STORE
- Only one XA resources have voted to COMMIT the transaction [ ==only 1 prepared XA transaction branch ]
- Therefore Wildfly has not written a COMMIT record to the Wildlfly LOG-STORE
- After a system crash the whole transaction should be rolled back
- After a system crash Periodic Recovery Thread should use XA recover and XA rollback to rollback this transaction without any user intervention
Prepare Testcase
Press Init Accounts :
11:04:21.039 Calling checkAccounts() in progress ...
11:04:21.088 Database Name:BANKA -- Account: User99_at_BANKA -- Balance: 10000.0
11:04:21.128 Database Name:BANKB -- Account: User98_at_BANKB -- Balance: 10000.0
11:04:21.128 Leaving checkAccounts() without any Error
Set Breakpoints : Class oracle.jdbc.xa.OracleXAResource : Methode entry for prepare(), commit() and rollback()
-> Don't forget to set breackpoint for rollback to avoid the Periodic Recovery Thread to rollback the
transaction before we have rebooted Wildfly server !
Start Application by pressing:
Use debugger and stop at Entry of 2.nd oracle.jdbc.xa.client.OracleXAResource.prepare call
Method breakpoint hit in oracle.jdbc.xa.client.OracleXAResource.prepare at line 693 by thread default task-6.
Method breakpoint hit in oracle.jdbc.xa.client.OracleXAResource.prepare at line 693 by thread default task-6.
Verify Wildfly LOG-STORE
[oracle@wls1 XA]$ $WILDFLY_HOME/bin/jboss-cli.sh --connect --file=xa.cli
{"outcome" => "success"}
-> No transaction reference in Wildfly LOG-STORE
SQL> select host_name,instance_name from v$instance;
HOST_NAME INSTANCE_NAME
------------------------------ ----------------
hract22.example.com bankA_1
SQL> select STATE, LOCAL_TRAN_ID, GLOBAL_TRAN_ID , to_char(FAIL_TIME,'DD-MON-YY HH24:MI:SS') FAIL_TIME,
2 to_char(FORCE_TIME,'DD-MON-YY HH24:MI:SS') FORCE_TIME, to_char(RETRY_TIME,'DD-MON-YY HH24:MI:SS') RETRY_TIME,
3 HOST, OS_USER, COMMIT# from dba_2pc_pending;
no rows selected
SQL> set echo off
Connected.
SQL> select host_name,instance_name from v$instance;
HOST_NAME INSTANCE_NAME
------------------------------ ----------------
hract22.example.com bankb_3
SQL> select STATE, LOCAL_TRAN_ID, GLOBAL_TRAN_ID , to_char(FAIL_TIME,'DD-MON-YY HH24:MI:SS') FAIL_TIME,
2 to_char(FORCE_TIME,'DD-MON-YY HH24:MI:SS') FORCE_TIME, to_char(RETRY_TIME,'DD-MON-YY HH24:MI:SS') RETRY_TIME,
3 HOST, OS_USER, COMMIT# from dba_2pc_pending;
STATE LOCAL_TRAN_ID GLOBAL_TRAN_ID
---------------- ---------------------- --------------------------------------------------------------------------------
FAIL_TIME FORCE_TIME RETRY_TIME HOST OS_USER COMMIT#
------------------ ------------------ ------------------ ------------------------- --------------- ----------------
prepared 14.2.389 131077.00000000000000000000FFFFC0A805C9614B927B5545C53A000000A631
03-MAY-15 09:23:47 03-MAY-15 09:26:12 wls1.example.com oracle 9848205
-> No entry in Wildfls LOG-STORE - only one Transaction Branch is prepared on instance bankb_3
Note : During testing Periodic recovery Threads stops at our breakpoint in oracle.jdbc.xa.client.OracleXAResource.rollback
"Periodic Recovery"
oracle.jdbc.xa.client.OracleXAResource.rollback(OracleXAResource.java:945)
org.jboss.jca.adapters.jdbc.xa.XAManagedConnection.rollback(XAManagedConnection.java:346)
org.jboss.jca.core.tx.jbossts.XAResourceWrapperImpl.rollback(XAResourceWrapperImpl.java:196)
com.arjuna.ats.internal.jta.recovery.arjunacore.XARecoveryModule.handleOrphan(XARecoveryModule.java:786)
com.arjuna.ats.internal.jta.recovery.arjunacore.XARecoveryModule.xaRecoverySecondPass(XARecoveryModule.java:692)
com.arjuna.ats.internal.jta.recovery.arjunacore.XARecoveryModule.bottomUpRecovery(XARecoveryModule.java:431)
com.arjuna.ats.internal.jta.recovery.arjunacore.XARecoveryModule.periodicWorkSecondPass(XARecoveryModule.java:212)
com.arjuna.ats.internal.arjuna.recovery.PeriodicRecovery.doWorkInternal(PeriodicRecovery.java:789)
com.arjuna.ats.internal.arjuna.recovery.PeriodicRecovery.run(PeriodicRecovery.java:371)
-> This means with a breakpoint at rollback() the Periodic Recovery Thread would have already
delete this XA transaction.
Now Kill Java program and verify whether records can be locked for DML operations:
[oracle@wls1 XA]$ ps -elf | grep java
0 S oracle 14493 14438 18 80 0 - 413832 futex_ 08:50 ? 00:07:13 /usr/java/latest/bin/java -D[Standalone] -Xms128m -Xmx512m -XX:MaxPermSize=256m -server ...
[oracle@wls1 XA]$ kill -9 14493
GLOBAL_NAME
--------------------------------------------------------------------------------
BANKA
ACCOUNT BALANCE
-------------------------------- ----------
User99_at_BANKA 10000
Connected.
--> No problem at bankA
GLOBAL_NAME
--------------------------------------------------------------------------------
BANKB
select * from accounts for update *
ERROR at line 1:
ORA-01591: lock held by in-doubt distributed transaction 14.2.389
-> As we have only one prepared Transaction - ORA-1591 comes up only at bankB
-> Now Restart Wildfly to fix the ORA-1591 problem
SQL> @check_it
GLOBAL_NAME
--------------------------------------------------------------------------------
BANKA
ACCOUNT BALANCE
-------------------------------- ----------
User99_at_BANKA 10000
GLOBAL_NAME
--------------------------------------------------------------------------------
BANKB
ACCOUNT BALANCE
-------------------------------- ----------
User98_at_BANKB 10000
-> After Wildfly restart the transaction was rolled back and we can access
both records for DML operations.
Heuristically committed transaction – A major Pitfall for DBAs
Assume we have following configruation:
System A : RAC database running database bankA running London
System B : RAC database running database bankB running in Frankfurt
System C : Wildfly application server
After a network outage all the systems are not able to communicate with each other but
these systems are still up and running.
Now System C - our Wildfly server - is crashing during a 2-Phase-Commit.
The crash occurs after having prepared both transaction an for database bankA, bankB
and Wildfly has already written a COMMIT record to its LOG-STAORE.
Setup testcase:
09:10:41.653 Calling checkAccounts() in progress ...
09:10:41.677 Database Name:BANKA -- Account: User99_at_BANKA -- Balance: 10000.0
09:10:41.717 Database Name:BANKB -- Account: User98_at_BANKB -- Balance: 10000.0
09:10:41.719 Leaving checkAccounts() without any Error
Setup testcase:
Tx Timeout : 60
-> Set breakpoint in commit() metode of oracle.jdbc.xa.OracleXAResource
Method breakpoint hit in oracle.jdbc.xa.client.OracleXAResource.commit at line 553 by thread default task-10.
Thread default task-10 stopped at OracleXAResource.java:553.
Start the application by pressing:
-> Wait until we get ORA-1591 accessing these records:
BANKA:
SQL> connect scott/tiger@ract2-scan.grid12c.example.com:1521/banka
SQL> select * from accounts for update;
select * from accounts for update *
ERROR at line 1:
ORA-01591: lock held by in-doubt distributed transaction 18.24.2100
BANKB:
SQL> connect scott/tiger@ract2-scan.grid12c.example.com:1521/bankb
Connected.
SQL> select * from accounts for update;
select * from accounts for update
ERROR at line 1:
ORA-01591: lock held by in-doubt distributed transaction 12.26.389
Now a lot of end users calls in and raise complaints to their DBAs that a major application running
on RAC DB bankA and RAC DB bankB are not working anymore.
The DBA on bankA ( London) checks dba_2pc_pending and found a prepared TX:
SQL> select STATE, LOCAL_TRAN_ID, GLOBAL_TRAN_ID , to_char(FAIL_TIME,'DD-MON-YY HH24:MI:SS') FAIL_TIME,
2 to_char(FORCE_TIME,'DD-MON-YY HH24:MI:SS') FORCE_TIME, to_char(RETRY_TIME,'DD-MON-YY HH24:MI:SS') RETRY_TIME,
3 HOST, OS_USER, COMMIT# from dba_2pc_pending;
STATE LOCAL_TRAN_ID GLOBAL_TRAN_ID
---------------- ---------------------- --------------------------------------------------------------------------------
FAIL_TIME FORCE_TIME RETRY_TIME HOST OS_USER COMMIT#
------------------ ------------------ ------------------ ------------------------- --------------- ----------------
prepared 18.24.2100 131077.00000000000000000000FFFFC0A805C9B989E289554386360000038B31
02-MAY-15 09:12:03 02-MAY-15 09:17:31 wls1.example.com oracle 24303841
The DBA on bankB ( Frankfurt ) checks dba_2pc_pending and found a prepared TX:
SQL> select STATE, LOCAL_TRAN_ID, GLOBAL_TRAN_ID , to_char(FAIL_TIME,'DD-MON-YY HH24:MI:SS') FAIL_TIME,
2 to_char(FORCE_TIME,'DD-MON-YY HH24:MI:SS') FORCE_TIME, to_char(RETRY_TIME,'DD-MON-YY HH24:MI:SS') RETRY_TIME,
3 HOST, OS_USER, COMMIT# from dba_2pc_pending;
STATE LOCAL_TRAN_ID GLOBAL_TRAN_ID
---------------- ---------------------- --------------------------------------------------------------------------------
FAIL_TIME FORCE_TIME RETRY_TIME HOST OS_USER COMMIT#
------------------ ------------------ ------------------ ------------------------- --------------- ----------------
prepared 12.26.389 131077.00000000000000000000FFFFC0A805C9B989E289554386360000038B31
02-MAY-15 09:12:02 02-MAY-15 09:18:30 wls1.example.com oracle 9400626
Now things goes wrong.
-> The database itself does not have any ideas whether Wildfly has written a COMMIT record to the LOG-STORE or not.
The DBAs doesn't check the Wildfly LOG-STORE
Due to the pressure to get ONLINE asap the DBA's does the following :
DBA for RAC DB bankA ( London)
SQL> connect sys/sys@ract2-scan.grid12c.example.com:1521/bankA as sysdba
Connected.
SQL> select STATE, LOCAL_TRAN_ID from dba_2pc_pending;
STATE LOCAL_TRAN_ID
---------------- ----------------------
prepared 18.24.2100
SQL> rollback force '18.24.2100';
Rollback complete.
DBA for RAC DB bankB ( Frankfurt)
SQL> connect sys/sys@ract2-scan.grid12c.example.com:1521/bankB as sysdba
Connected.
SQL> select STATE, LOCAL_TRAN_ID from dba_2pc_pending;
STATE LOCAL_TRAN_ID
---------------- ----------------------
prepared 12.26.389
SQL> commit force '12.26.389';
Commit complete.
Now both DBAs verify whether we can access these records for DML operation
SQL> connect scott/tiger@ract2-scan.grid12c.example.com:1521/banka
Connected.
SQL>
SQL> select * from accounts for update;
ACCOUNT BALANCE
-------------------------------- ----------
User99_at_BANKA 10000
SQL> connect scott/tiger@ract2-scan.grid12c.example.com:1521/bankb
SQL> select * from accounts for update;
ACCOUNT BALANCE
-------------------------------- ----------
User98_at_BANKB 14000
-> Works fine - really ?
The DBAs have logically corrupted ozr system integrity !!
We have successfully added the money to User98_at_BANKB but not reduced the money
from account User99_at_BANKA !
Note this should never happen at your production system - or you may loose your DBA job very easily !!
How we can avoid this problem ?
1. Check whether Wildfly has written a commit record
[oracle@wls1 XA]$ $WILDFLY_HOME/bin/jboss-cli.sh --connect --file=xa.cli
{"outcome" => "success"}
0:ffffc0a805c9:-46761d77:55438636:38b
--> There is a COMMIT record for XID : 0:ffffc0a805c9:-46761d77:55438636:38b - so it is save to call :
COMMIT FORCE 'Local_Tran_ID'
If we can't find the XID within Wildfly it is save to call on all involved databases :
ROLLBACK FORCE 'Local_Tran_ID';
A closer look on Exceptions
ut.commit() fail with :
TX Info:
10:21:58.711 No TX active - before ut.begin() TX status: 6 - STATUS_NO_TRANSACTION - Transaction Timeout: 60
10:21:58.720 Setting New Tansaction Timeout : 60
10:22:04.640 After ut.begin() TX status: 0 - STATUS_ACTIVE
10:22:04.709 Tx status before ut.commit() - TX status: 0 - STATUS_ACTIVE - Now sleeping : 1 [ seconds]
10:28:03.579 FATAL ERROR:
10:28:03.582 Tx status after Exception before ut.rollback(): TX status: 6 - STATUS_NO_TRANSACTION - Exception: null
10:28:03.585 Tx status after Exception: FATAL error during ut.rollback() :TX status: 6 - STATUS_NO_TRANSACTION
Java Code throws javax.transaction.HeuristicMixedException :
10:28:03.558 Error in JSF top level function: transferMoney() - Need to ROLLBACK transaction
10:28:03.576 javax.transaction.HeuristicMixedException
at com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple.commitAndDisassociate(TransactionImple.java:1174)
at com.arjuna.ats.internal.jta.transaction.arjunacore.BaseTransaction.commit(BaseTransaction.java:126)
at com.arjuna.ats.jbossatx.BaseTransactionManagerDelegate.commit(BaseTransactionManagerDelegate.java:75)
at org.jboss.tm.usertx.client.ServerVMClientUserTransaction.commit(ServerVMClientUserTransaction.java:173)
at com.hhu.XaTestBean.transferMoney(XaTestBean.java:778)
Wildfly server.log reports :
2015-05-02 10:27:56,445 WARN [com.arjuna.ats.jta] (default task-1)
ARJUNA016036: commit on < formatId=131077, gtrid_length=29, bqual_length=36,
tx_uid=0:ffffc0a805c9:-46761d77:55438636:4d3, node_name=1,
branch_uid=0:ffffc0a805c9:-46761d77:55438636:4d5, subordinatenodename=null,
eis_name=java:jboss/datasources/xa_rac12g_bankb > (XAResourceWrapperImpl@172657b2[xaResource=org.jboss.jca.adapters.jdbc.xa.XAManagedConnection@489e2cb1 pad=false
overrideRmValue=null productName=Oracle productVersion=Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production
With the Partitioning, Real Application Clusters, Automatic Storage Management, OLAP,
Advanced Analytics and Real Application Testing options
jndiName=java:jboss/datasources/xa_rac12g_bankb])
failed with exception $XAException.XAER_NOTA: oracle.jdbc.xa.OracleXAException
at oracle.jdbc.driver.T4CXAResource.kputxrec(T4CXAResource.java:965)
at oracle.jdbc.driver.T4CXAResource.doCommit(T4CXAResource.java:454)
at oracle.jdbc.xa.client.OracleXAResource.commit(OracleXAResource.java:583)
at org.jboss.jca.adapters.jdbc.xa.XAManagedConnection.commit(XAManagedConnection.java:338)
at org.jboss.jca.core.tx.jbossts.XAResourceWrapperImpl.commit(XAResourceWrapperImpl.java:107)
at com.arjuna.ats.internal.jta.resources.arjunacore.XAResourceRecord.topLevelCommit(XAResourceRecord.java:461)
at com.arjuna.ats.arjuna.coordinator.BasicAction.doCommit(BasicAction.java:2810)
at com.arjuna.ats.arjuna.coordinator.BasicAction.doCommit(BasicAction.java:2726)
at com.arjuna.ats.arjuna.coordinator.BasicAction.phase2Commit(BasicAction.java:1820)
at com.arjuna.ats.arjuna.coordinator.BasicAction.End(BasicAction.java:1504)
at com.arjuna.ats.arjuna.coordinator.TwoPhaseCoordinator.end(TwoPhaseCoordinator.java:96)
at com.arjuna.ats.arjuna.AtomicAction.commit(AtomicAction.java:162)
at com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple.commitAndDisassociate(TransactionImple.java:1166)
at com.arjuna.ats.internal.jta.transaction.arjunacore.BaseTransaction.commit(BaseTransaction.java:126)
at com.arjuna.ats.jbossatx.BaseTransactionManagerDelegate.commit(BaseTransactionManagerDelegate.java:75)
at org.jboss.tm.usertx.client.ServerVMClientUserTransaction.commit(ServerVMClientUserTransaction.java:173)
at com.hhu.XaTestBean.transferMoney(XaTestBean.java:778) [classes:]
-> JAVA Code is failing with $XAException.XAER_NOTA: oracle.jdbc.xa.OracleXAException as the related XA transaction is
already committed using COMMIT FORCE 'Local_Tran_ID' or rolled back using ROLLBACK force 'Local_Tran_ID' at Database level.
Cleanup system state after above getting a heuristically committed XA transaction
- Try to repair the logical corruption from any logfile we can find tracking SQL change and TX id.
- Cleanup the system [ Wildfly LOG-STORE and Oracle dba_2pc_pending table ]
Cleanup Wildfly LOG-STORE
Verify currents XA transaction status
[oracle@wls1 XA]$ $WILDFLY_HOME/bin/jboss-cli.sh --connect --file=xa.cli
{"outcome" => "success"}
0:ffffc0a805c9:-46761d77:55438636:38b
0:ffffc0a805c9:-46761d77:55438636:439
0:ffffc0a805c9:-46761d77:55438636:4d3
SQL> connect sys/sys@ract2-scan.grid12c.example.com:1521/bankb as sysdba
SQL> select STATE, LOCAL_TRAN_ID, GLOBAL_TRAN_ID from dba_2pc_pending;
STATE LOCAL_TRAN_ID GLOBAL_TRAN_ID
---------------- ---------------------- ----------------------------------------------------------------------
forced commit 12.26.389 131077.00000000000000000000FFFFC0A805C9B989E289554386360000038B31
forced commit 17.21.390 131077.00000000000000000000FFFFC0A805C9B989E289554386360000043931
forced commit 14.26.382 131077.00000000000000000000FFFFC0A805C9B989E28955438636000004D331
SQL> connect sys/sys@ract2-scan.grid12c.example.com:1521/banka as sysdba
SQL> select STATE, LOCAL_TRAN_ID, GLOBAL_TRAN_ID from dba_2pc_pending;
STATE LOCAL_TRAN_ID GLOBAL_TRAN_ID
---------------- ---------------------- ----------------------------------------------------------------------
forced rollback 18.24.2100 131077.00000000000000000000FFFFC0A805C9B989E289554386360000038B31
forced rollback 14.1.2092 131077.00000000000000000000FFFFC0A805C9B989E289554386360000043931
forced rollback 16.4.2077 131077.00000000000000000000FFFFC0A805C9B989E28955438636000004D331
Purge the XA transaction from Wildfly LOG-STORE
$WILDFLY_HOME/bin/jboss-cli.sh --connect
"/subsystem=transactions/log-store=log-store/transactions=0\:ffffc0a805c9\:-46761d77\:55438636\:38b:delete"
{"outcome" => "success"}
[oracle@wls1 XA]$ $WILDFLY_HOME/bin/jboss-cli.sh --connect
"/subsystem=transactions/log-store=log-store/transactions=0\:ffffc0a805c9\:-46761d77\:55438636\:439:delete"
{"outcome" => "success"}
[oracle@wls1 XA]$ $WILDFLY_HOME/bin/jboss-cli.sh --connect
"/subsystem=transactions/log-store=log-store/transactions=0\:ffffc0a805c9\:-46761d77\:55438636\:4d3:delete"
{"outcome" => "success"}
[oracle@wls1 XA]$ $WILDFLY_HOME/bin/jboss-cli.sh --connect --file=xa.cli
{"outcome" => "success"}
Finally purge XA transactions at DB level run on both RAC nodes:
SQL> execute DBMS_TRANSACTION.PURGE_MIXED('18.24.2100');
SQL> execute DBMS_TRANSACTION.PURGE_MIXED('14.1.2092');
SQL> execute DBMS_TRANSACTION.PURGE_MIXED('16.4.2077');
Understanding ORA-1591 : lock held by in-doubt distributed transaction
Change Transaction Timeout: 60
Set Breakpoints in : oracle.jdbc.xa.OracleXAResource prepare() and commit() functions
Press F5 untill we reach first Commit breakpoint:
Method breakpoint hit in oracle.jdbc.xa.client.OracleXAResource.prepare at line 693 by thread default task-1.
Method breakpoint hit in oracle.jdbc.xa.client.OracleXAResource.prepare at line 693 by thread default task-1.
Method breakpoint hit in oracle.jdbc.xa.client.OracleXAResource.commit at line 553 by thread default task-1.
Press :
Transaction status - immediate after stopping:
Connected.
SQL> select host_name,instance_name from v$instance;
HOST_NAME INSTANCE_NAME
------------------------------ ----------------
hract22.example.com bankA_2
SQL> select * from sys.dba_pending_transactions;
FORMATID GLOBALID BRANCHID
---------- ------------------------------------------------------------ -------------------------------------------------------
131077 00000000000000000000FFFFC0A805C9B989E289554386360000005931 00000000000000000000FFFFC0A805C9B989E289554386360000005
E0000000200000000
SQL> select STATE, LOCAL_TRAN_ID, GLOBAL_TRAN_ID , to_char(FAIL_TIME,'DD-MON-YY HH24:MI:SS') FAIL_TIME,
2 to_char(FORCE_TIME,'DD-MON-YY HH24:MI:SS') FORCE_TIME, to_char(RETRY_TIME,'DD-MON-YY HH24:MI:SS') RETRY_TIME,
3 HOST, OS_USER, COMMIT# from dba_2pc_pending;
no rows selected
SQL> select host_name,instance_name from v$instance;
HOST_NAME INSTANCE_NAME
------------------------------ ----------------
hract22.example.com bankb_3
SQL> select * from sys.dba_pending_transactions;
FORMATID GLOBALID BRANCHID
---------- ------------------------------------------------------------ -------------------------------------------------------
131077 00000000000000000000FFFFC0A805C9B989E289554386360000005931 00000000000000000000FFFFC0A805C9B989E289554386360000005
B0000000300000000
SQL> select STATE, LOCAL_TRAN_ID, GLOBAL_TRAN_ID , to_char(FAIL_TIME,'DD-MON-YY HH24:MI:SS') FAIL_TIME,
2 to_char(FORCE_TIME,'DD-MON-YY HH24:MI:SS') FORCE_TIME, to_char(RETRY_TIME,'DD-MON-YY HH24:MI:SS') RETRY_TIME,
3 HOST, OS_USER, COMMIT# from dba_2pc_pending;
no rows selected
Script to track XA transactions within Wildfly:
[oracle@wls1 XA]$ cat xa.cli
/subsystem=transactions/log-store=log-store/:probe()
ls /subsystem=transactions/log-store=log-store/transactions
[oracle@wls1 XA]$ $WILDFLY_HOME/bin/jboss-cli.sh --connect --file=xa.cli
{"outcome" => "success"}
0:ffffc0a805c9:-46761d77:55438636:59
-> The prepared transaction branchess are visible in sys.dba_pending_transactions - but not yet in dba_2pc_pending
Wildfly reports the transaction in its LOG-STORE
Now wait 60 seconds until Oracle times out the XA Transaction and creates an in-doubt transaction
SQL> select host_name,instance_name from v$instance;
HOST_NAME INSTANCE_NAME
------------------------------ ----------------
hract22.example.com bankA_2
SQL> select * from sys.dba_pending_transactions;
FORMATID GLOBALID BRANCHID
---------- ------------------------------------------------------------ -------------------------------------------------------
131077 00000000000000000000FFFFC0A805C9B989E289554386360000005931 00000000000000000000FFFFC0A805C9B989E289554386360000005
E0000000200000000
SQL> select STATE, LOCAL_TRAN_ID, GLOBAL_TRAN_ID , to_char(FAIL_TIME,'DD-MON-YY HH24:MI:SS') FAIL_TIME,
2 to_char(FORCE_TIME,'DD-MON-YY HH24:MI:SS') FORCE_TIME, to_char(RETRY_TIME,'DD-MON-YY HH24:MI:SS') RETRY_TIME,
3 HOST, OS_USER, COMMIT# from dba_2pc_pending;
STATE LOCAL_TRAN_ID GLOBAL_TRAN_ID
---------------- ---------------------- --------------------------------------------------------------------------------
FAIL_TIME FORCE_TIME RETRY_TIME HOST OS_USER COMMIT#
------------------ ------------------ ------------------ ------------------------- --------------- ----------------
prepared 3.18.4315 131077.00000000000000000000FFFFC0A805C9B989E289554386360000005931
01-MAY-15 16:15:02 01-MAY-15 16:15:35 wls1.example.com oracle 24161321
SQL> select host_name,instance_name from v$instance;
HOST_NAME INSTANCE_NAME
------------------------------ ----------------
hract22.example.com bankb_3
SQL> select * from sys.dba_pending_transactions;
FORMATID GLOBALID BRANCHID
---------- ------------------------------------------------------------ -------------------------------------------------------
131077 00000000000000000000FFFFC0A805C9B989E289554386360000005931 00000000000000000000FFFFC0A805C9B989E289554386360000005
B0000000300000000
SQL> select STATE, LOCAL_TRAN_ID, GLOBAL_TRAN_ID , to_char(FAIL_TIME,'DD-MON-YY HH24:MI:SS') FAIL_TIME,
2 to_char(FORCE_TIME,'DD-MON-YY HH24:MI:SS') FORCE_TIME, to_char(RETRY_TIME,'DD-MON-YY HH24:MI:SS') RETRY_TIME,
3 HOST, OS_USER, COMMIT# from dba_2pc_pending;
STATE LOCAL_TRAN_ID GLOBAL_TRAN_ID
---------------- ---------------------- --------------------------------------------------------------------------------
FAIL_TIME FORCE_TIME RETRY_TIME HOST OS_USER COMMIT#
------------------ ------------------ ------------------ ------------------------- --------------- ----------------
prepared 15.23.381 131077.00000000000000000000FFFFC0A805C9B989E289554386360000005931
01-MAY-15 16:15:00 01-MAY-15 16:15:51 wls1.example.com oracle 9263234
-> The prepared transaction branches are visible in sys.dba_pending_transactions and dba_2pc_pending
Wildfly reports the transactions as stored in the log store !
Note: Accessing a record being part of a prepared distributed transaction will throw ORA-1591
and print out the local transaction ID.
Connected.
select * from accounts for update *
ERROR at line 1:
ORA-01591: lock held by in-doubt distributed transaction 3.18.4315
Connected.
select * from accounts for update
ERROR at line 1:
ORA-01591: lock held by in-doubt distributed transaction 15.23.381
Now Press F5 again until we get back control via our HTML page
Note: Even the entry was in dba_2pc_pending running the commit sucessfully finshed the distributed transaction
Entries in sys.dba_pending_transactions and dba_2pc_pending are removed and Wildfly LOG-STORE
was cleaned up.
Understanding ORA-2049 error: distributed transaction waiting for lock
Lock a record via sqlplus
SQL> select BALANCE from Accounts for update;
BALANCE
----------
10000
Press:
Generic Error:
17:05:19.248 Error in JSF top level function: transferMoney() - Need to ROLLBACK transaction
17:05:19.248 ORA-02049: timeout: distributed transaction waiting for lock
17:05:19.249 java.sql.SQLSyntaxErrorException: ORA-02049: timeout: distributed transaction waiting for lock
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:450)
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:399)
at oracle.jdbc.driver.T4C8Oall.processError(T4C8Oall.java:1059)
at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:522)
at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:257)
at oracle.jdbc.driver.T4C8Oall.doOALL(T4C8Oall.java:587)
at oracle.jdbc.driver.T4CStatement.doOall8(T4CStatement.java:210)
at oracle.jdbc.driver.T4CStatement.doOall8(T4CStatement.java:30)
at oracle.jdbc.driver.T4CStatement.executeForRows(T4CStatement.java:931)
at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1150)
at oracle.jdbc.driver.OracleStatement.executeUpdateInternal(OracleStatement.java:1707)
at oracle.jdbc.driver.OracleStatement.executeUpdate(OracleStatement.java:1670)
at oracle.jdbc.driver.OracleStatementWrapper.executeUpdate(OracleStatementWrapper.java:310)
at org.jboss.jca.adapters.jdbc.WrappedStatement.executeUpdate(WrappedStatement.java:375)
at com.hhu.XaTestBean.doUpdate(XaTestBean.java:603)
Transaction status
17:04:18.911 No TX active - before ut.begin() TX status: 6 - STATUS_NO_TRANSACTION - Transaction Timeout: 5
17:04:18.911 Setting New Tansaction Timeout : 5
17:04:18.990 After ut.begin() TX status: 0 - STATUS_ACTIVE
17:05:19.250 FATAL ERROR:
17:05:19.250 Tx status after Exception before ut.rollback(): TX status: 9 - STATUS_ROLLING_BACK
- Exception: ORA-02049: timeout: distributed transaction waiting for lock
17:05:19.284 Tx status after Exception - but successful ut.rollback(): TX status: 6 - STATUS_NO_TRANSACTION
Verify the related database init.ora parameter
SQL> show parameter distributed_lock_timeout
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
distributed_lock_timeout integer 60
Note: It's not a good idea to increase distributed_lock_timeout without understanding what is going on.
In a first approach try to re-run the failed code.
Any OLTP application should not lock a record for more than 60 seconds
It if a session times out within Oracle, then the Oracle database’s Process Monitor (PMON) will delete
the transaction branch if not prepared or cause the transaction to become in-doubt if prepared.
Generic Timeout Settings : Transaction Timeout for JDBC < DISTRIBUTED_LOCK_TIMEOUT .
Explanation:
- The java code tries to lock the record which is locked by our sqlplus session
- After waiting 60 seconds the SQL processing returns an exception:
java.sql.SQLSyntaxErrorException: ORA-02049: timeout: distributed transaction waiting for lock
- This wait time is controlled by the init.ora parameter distributed_lock_timeout
SQL> show parameter distributed_lock_timeout
NAME TYPE VALUE
—————————— ———– —————-
distributed_lock_timeout integer 60
- As for any other exception the XA transactions needs to be rolled back.
Understanding Transaction Timeout
Setup testcase:
Transaction Timeout: 5
Sleep Time: 10
Press :
Error Stack
17:53:21.729 Error in JSF top level function: transferMoney() - Need to ROLLBACK transaction
17:53:21.729 ARJUNA016102: The transaction is not active! Uid is 0:ffffc0a805c9:410b1af0:55424451:c6
17:53:21.730 javax.transaction.RollbackException: ARJUNA016102: The transaction is not active! Uid is 0:ffffc0a805c9:410b1af0:55424451:c6
at com.arjuna.ats.internal.jta.transaction.arjunacore.TransactionImple.commitAndDisassociate(TransactionImple.java:1156)
at com.arjuna.ats.internal.jta.transaction.arjunacore.BaseTransaction.commit(BaseTransaction.java:126)
at com.arjuna.ats.jbossatx.BaseTransactionManagerDelegate.commit(BaseTransactionManagerDelegate.java:75)
at org.jboss.tm.usertx.client.ServerVMClientUserTransaction.commit(ServerVMClientUserTransaction.java:173)
at com.hhu.XaTestBean.transferMoney(XaTestBean.java:769)
Tx Status:
17:53:11.556 No TX active - before ut.begin() TX status: 6 - STATUS_NO_TRANSACTION - Transaction Timeout: 5
17:53:11.556 Setting New Tansaction Timeout : 5
17:53:11.563 After ut.begin() TX status: 0 - STATUS_ACTIVE
17:53:11.726 Tx status before ut.commit() - TX status: 0 - STATUS_ACTIVE - Now sleeping : 10 [ seconds]
17:53:21.730 FATAL ERROR:
17:53:21.730 Tx status after Exception before ut.rollback(): TX status: 6 - STATUS_NO_TRANSACTION -
Exception: ARJUNA016102: The transaction is not active! Uid is 0:ffffc0a805c9:410b1af0:55424451:c6
17:53:21.730 Tx status after Exception: FATAL error during ut.rollback() :TX status: 6 - STATUS_NO_TRANSACTION -
Exception: java.lang.IllegalStateException: BaseTransaction.rollback - ARJUNA016074: no transaction!
Note :
Transaction Timeout for JDBC < DISTRIBUTED_LOCK_TIMEOUT ( default : 60 seconds ) .
Code Explanation:
- After 10 seconds [ our sleeptime ] the transaction processing step has finished and ut.commit() is called
- Because we have reached transaction Timeout of 5 seconds the ut.commit() call fails
- In the exception handler the ut.rollback() call becomes a NOOP as the parent transaction was already deleted.
Debugging UserTransaction API and XAResource API by using a Netbeans Debugger
Using Netbeans to debug the JAVA XA code more in detail
1. Start Wildfly in Debug Mode
2. Add Breakpoint:
Debugger : JAVA Breakpoint Type : Methode
Class Name : oracle.jdbc.xa.OracleXAResource
Check : All methods for a given Class
Method Name: <all methods>
Stop on : Method Entry
3. Debug -> Debug Main Program
4. Set breakpoint at line ut.begin()
5. Debug -> Run to Cursor
6. Press F5 until our HTML page gets back control
Breakpoint hit at line 762 in class com.hhu.XaTestBean by thread default task-15.
Thread default task-15 stopped at XaTestBean.java:762.
ut.beginn() translates to the following XA calls
Method breakpoint hit in oracle.jdbc.xa.OracleXAResource.setTransactionTimeout at line 1063 by thread default task-15.
Method breakpoint hit in oracle.jdbc.xa.client.OracleXAResource.start at line 163 by thread default task-15.
Method breakpoint hit in oracle.jdbc.xa.OracleXAResource.saveAndAlterAutoCommitModeForGlobalTransaction at line 797 by thread default task-15.
Method breakpoint hit in oracle.jdbc.xa.OracleXAResource.checkError at line 1188 by thread default task-15.
Method breakpoint hit in oracle.jdbc.xa.OracleXAResource.createOrUpdateXid at line 296 by thread default task-15.
Method breakpoint hit in oracle.jdbc.xa.OracleXAResource.getMatchingXidListEntry at line 234 by thread default task-15.
Method breakpoint hit in oracle.jdbc.xa.OracleXAResource.enterGlobalTxnMode at line 1179 by thread default task-15.
Method breakpoint hit in oracle.jdbc.xa.OracleXAResource.isSameRM at line 995 by thread default task-15.
-> Transaction for bankA is started, isSameRM() is used to check whether bankA and bankB are using the same Resource Manager
As we have 2 different RAC systems this call should return false !
Method breakpoint hit in oracle.jdbc.xa.OracleXAResource.getConnection at line 945 by thread default task-15.
Method breakpoint hit in oracle.jdbc.xa.OracleXAResource.setTransactionTimeout at line 1063 by thread default task-15.
Method breakpoint hit in oracle.jdbc.xa.client.OracleXAResource.start at line 163 by thread default task-15.
Method breakpoint hit in oracle.jdbc.xa.OracleXAResource.saveAndAlterAutoCommitModeForGlobalTransaction at line 797 by thread default task-15.
Method breakpoint hit in oracle.jdbc.xa.OracleXAResource.checkError at line 1188 by thread default task-15.
Method breakpoint hit in oracle.jdbc.xa.OracleXAResource.createOrUpdateXid at line 296 by thread default task-15.
Method breakpoint hit in oracle.jdbc.xa.OracleXAResource.getMatchingXidListEntry at line 234 by thread default task-15.
Method breakpoint hit in oracle.jdbc.xa.OracleXAResource.enterGlobalTxnMode at line 1179 by thread default task-15.
-> Transaction for bankB is started
ut.commit() translates to the following XA calls
Method breakpoint hit in oracle.jdbc.xa.client.OracleXAResource.end at line 358 by thread default task-15.
Method breakpoint hit in oracle.jdbc.xa.OracleXAResource.suspendStacked at line 430 by thread default task-15.
Method breakpoint hit in oracle.jdbc.xa.OracleXAResource.isSameXid at line 256 by thread default task-15.
Method breakpoint hit in oracle.jdbc.xa.OracleXAResource.isXidSuspended at line 398 by thread default task-15.
Method breakpoint hit in oracle.jdbc.xa.OracleXAResource.getMatchingXidListEntry at line 234 by thread default task-15.
Method breakpoint hit in oracle.jdbc.xa.OracleXAResource.removeXidFromList at line 244 by thread default task-15.
Method breakpoint hit in oracle.jdbc.xa.OracleXAResource.isSameXid at line 256 by thread default task-15.
Method breakpoint hit in oracle.jdbc.xa.OracleXAResource.isXidListEmpty at line 274 by thread default task-15.
Method breakpoint hit in oracle.jdbc.xa.OracleXAResource.exitGlobalTxnMode at line 1167 by thread default task-15.
Method breakpoint hit in oracle.jdbc.xa.OracleXAResource.checkError at line 1188 by thread default task-15.
Method breakpoint hit in oracle.jdbc.xa.OracleXAResource.restoreAutoCommitModeForGlobalTransaction at line 768 by thread default task-15.
Method breakpoint hit in oracle.jdbc.xa.client.OracleXAResource.prepare at line 693 by thread default task-15.
-> For bankA the XA transaction is finished by OracleXAResource.end() and prepared by OracleXAResource.prepare()
Method breakpoint hit in oracle.jdbc.xa.client.OracleXAResource.end at line 358 by thread default task-15.
Method breakpoint hit in oracle.jdbc.xa.OracleXAResource.suspendStacked at line 430 by thread default task-15.
Method breakpoint hit in oracle.jdbc.xa.OracleXAResource.isSameXid at line 256 by thread default task-15.
Method breakpoint hit in oracle.jdbc.xa.OracleXAResource.isXidSuspended at line 398 by thread default task-15.
Method breakpoint hit in oracle.jdbc.xa.OracleXAResource.getMatchingXidListEntry at line 234 by thread default task-15.
Method breakpoint hit in oracle.jdbc.xa.OracleXAResource.removeXidFromList at line 244 by thread default task-15.
Method breakpoint hit in oracle.jdbc.xa.OracleXAResource.isSameXid at line 256 by thread default task-15.
Method breakpoint hit in oracle.jdbc.xa.OracleXAResource.isXidListEmpty at line 274 by thread default task-15.
Method breakpoint hit in oracle.jdbc.xa.OracleXAResource.exitGlobalTxnMode at line 1167 by thread default task-15.
Method breakpoint hit in oracle.jdbc.xa.OracleXAResource.checkError at line 1188 by thread default task-15.
Method breakpoint hit in oracle.jdbc.xa.OracleXAResource.restoreAutoCommitModeForGlobalTransaction at line 768 by thread default task-15.
Method breakpoint hit in oracle.jdbc.xa.client.OracleXAResource.prepare at line 693 by thread default task-15.
--> For bankB the XA transaction is finished by OracleXAResource.end() and prepared by OracleXAResource.prepare()
Now as all XA branches are prepared the global transaction can be commited !
Method breakpoint hit in oracle.jdbc.xa.client.OracleXAResource.commit at line 553 by thread default task-15.
Method breakpoint hit in oracle.jdbc.xa.OracleXAResource.removeXidFromList at line 244 by thread default task-15.
Method breakpoint hit in oracle.jdbc.xa.OracleXAResource.isSameXid at line 256 by thread default task-15.
Method breakpoint hit in oracle.jdbc.xa.OracleXAResource.exitGlobalTxnMode at line 1167 by thread default task-15.
--> Commit XA transaction at bankA
Method breakpoint hit in oracle.jdbc.xa.client.OracleXAResource.commit at line 553 by thread default task-15.
Method breakpoint hit in oracle.jdbc.xa.OracleXAResource.removeXidFromList at line 244 by thread default task-15.
Method breakpoint hit in oracle.jdbc.xa.OracleXAResource.isSameXid at line 256 by thread default task-15.
Method breakpoint hit in oracle.jdbc.xa.OracleXAResource.exitGlobalTxnMode at line 1167 by thread default task-15.
--> Commit XA transaction at bankB
Note : The Periodic Recovery Thread is also using our XAResources and tries to complete any failed XA transaction
SQL Scripts and Wildfly Scripts
Create tables : cr_tab.sql
connect scott/tiger@ract2-scan.grid12c.example.com:1521/banka
drop table ACCOUNTS;
CREATE TABLE ACCOUNTS
( ACCOUNT varchar2(32) CONSTRAINT ACCOUNTS_PK PRIMARY KEY,
BALANCE NUMBER(10,2) constraint S_LOWER_CHK check (BALANCE >= 0)
);
insert into ACCOUNTS values ('User99_at_BANKA', 10000);
commit;
select * from global_name;
select * from accounts;
update Accounts set balance = balance - 11000;
rollback;
connect scott/tiger@ract2-scan.grid12c.example.com:1521/bankb
drop table ACCOUNTS;
CREATE TABLE ACCOUNTS
( ACCOUNT varchar2(32) CONSTRAINT ACCOUNTS_PK PRIMARY KEY,
BALANCE NUMBER(10,2) constraint S_LOWER_CHK check (BALANCE >= 0)
);
insert into ACCOUNTS values ('User98_at_BANKB', 10000);
commit;
select global_name from global_name;
select * from accounts;
update Accounts set balance = balance - 11000;
rollback;
Test whether the records are locked : check_it.sql
connect scott/tiger@ract2-scan.grid12c.example.com:1521/banka
select * from global_name;
select * from accounts for update;
connect scott/tiger@ract2-scan.grid12c.example.com:1521/bankb
select global_name from global_name;
select * from accounts for update;
Generic script the test XA staus of both RAC databases: check_xa.sql
set echo on
connect sys/sys@ract2-scan.grid12c.example.com:1521/banka as sysdba
@tx
connect sys/sys@ract2-scan.grid12c.example.com:1521/bankb as sysdba
@tx
exit
Script to display database details for distributed transactions: tx.sql
/*
If you run a 2PC you need to run tx.sql on both RAC databases .
Sample:
sqlplus sys/sys@ract2-scan.grid12c.example.com:1521/banka as sysdba @tx
sqlplus sys/sys@ract2-scan.grid12c.example.com:1521/bankb as sysdba @tx
*/
set echo off
set linesize 132
set pagesize 132
col HOST_NAME format A30
col GLOBAL_TRAN_ID format A80
col GLOBALID format A60
col BRANCHID format A55
col HOST format A25
col OS_USER format A15
set echo on
select host_name,instance_name from v$instance;
select * from sys.dba_pending_transactions;
select STATE, LOCAL_TRAN_ID, GLOBAL_TRAN_ID , to_char(FAIL_TIME,'DD-MON-YY HH24:MI:SS') FAIL_TIME,
to_char(FORCE_TIME,'DD-MON-YY HH24:MI:SS') FORCE_TIME, to_char(RETRY_TIME,'DD-MON-YY HH24:MI:SS') RETRY_TIME,
HOST, OS_USER, COMMIT# from dba_2pc_pending;
set echo off
Script to track XA transactions within Wildfly:
[oracle@wls1 XA]$ cat xa.cli
/subsystem=transactions/log-store=log-store/:probe()
ls /subsystem=transactions/log-store=log-store/transactions
Source Code
JSF Code:
bankInfo.xhtml - JSF page to display Transaction Info, Exception Info, failed SQL
index.xhtml - Main Entry JSF page - displays Account details
jdbc_info.xhtml - JSF Page to diplay JDBC driver details, Exceptions, Transaction processing Steps
rac_info.xhtml - JSF Page to display RAC instances details
Maven Project Object Model
pom.xml
JAVA Code:
XaTestBean.java
Reference