9 Replies Latest reply on Mar 28, 2007 6:34 PM by Charles Lamb

    strange XAEnvironment recovery behavior

      XAEnvironment (je-3.2.21 on Windows XP) is behaving strangely upon the recovery of
      database records that were prepared but not committed to a Database in a previous session.

      To reproduce the problem just run the main method in the test code below 3 times.

      On the first run, 4 transaction branches are created. Each branch puts a key-value
      pair to the database and is prepared. Two of the branches are left in their prepared
      state as the test program abruptly quits, thereby simulating a power failure or crash.

      On the second run, the main method calls runSecond() which recovers the two Xids that were
      prepared. Also, the data that was prepared is retrieved and verified. Everything is OK
      so far. However, we crash the database again before committing or rolling back the
      two recovered branches.

      I would expect that on the second attempt at recovering the prepared branches that we
      would again be able to retrieve and verify the kay-value pairs that we prepared into
      the database. The two Xids are still there but the key-value pairs
      seemed to have disappeared.

      Is this behavior proper?



      The following are the outputs from the three consecutive runs:

      run #1:

      starting runFirst()

      run #2:

      starting runSecond()
      Recovering: {globalTransactionId=tx3, branchQualifier=tx3_branch, formatId=0}
      Recovered data: tx3_branch
      Recovering: {globalTransactionId=tx4, branchQualifier=tx4_branch, formatId=0}
      Recovered data: tx4_branch

      run #3:

      starting runSecond()
      Recovering: {globalTransactionId=tx3, branchQualifier=tx3_branch, formatId=0}
      Failed to find data for xid {globalTransactionId=tx3, branchQualifier=tx3_branch, formatId=0}
      Recovering: {globalTransactionId=tx4, branchQualifier=tx4_branch, formatId=0}
      Failed to find data for xid {globalTransactionId=tx4, branchQualifier=tx4_branch, formatId=0}


      The test code:

      import java.io.File;
      import java.io.UnsupportedEncodingException;

      import javax.transaction.xa.XAException;
      import javax.transaction.xa.XAResource;
      import javax.transaction.xa.Xid;

      import com.sleepycat.je.Database;
      import com.sleepycat.je.DatabaseConfig;
      import com.sleepycat.je.DatabaseEntry;
      import com.sleepycat.je.DatabaseException;
      import com.sleepycat.je.EnvironmentConfig;
      import com.sleepycat.je.LockMode;
      import com.sleepycat.je.OperationStatus;
      import com.sleepycat.je.XAEnvironment;

      public class TestBerkeleyDBRecovery {

           public static final String TEST_DB_NAME = "test_recovery_db";
           private XAEnvironment env;
           private Database db;
           private Xid xid1;
           private Xid xid2;
           private Xid xid3;
           private Xid xid4;
           public TestBerkeleyDBRecovery(File envHomeDir) throws DatabaseException{
                EnvironmentConfig envConfig = new EnvironmentConfig();
                env = new XAEnvironment(envHomeDir, envConfig);
                DatabaseConfig dbConfig = new DatabaseConfig();
                db = env.openDatabase(null, TEST_DB_NAME, dbConfig);
                xid1 = new XidImpl("tx1", "tx1_branch", 0);
                xid2 = new XidImpl("tx2", "tx2_branch", 0);
                xid3 = new XidImpl("tx3", "tx3_branch", 0);
                xid4 = new XidImpl("tx4", "tx4_branch", 0);
           public void runFirst(){
                System.out.println("starting runFirst()");
                try {               
                     DatabaseEntry key = new DatabaseEntry();
                     DatabaseEntry value = new DatabaseEntry();
                     // do work for branch 1
                     env.start(xid1, XAResource.TMNOFLAGS);
                     db.put(null, key, value);
                     env.end(xid1, XAResource.TMSUCCESS);
                     // do work for branch 2
                     env.start(xid2, XAResource.TMNOFLAGS);
                     db.put(null, key, value);
                     env.end(xid2, XAResource.TMSUCCESS);
                     // do work for branch 3
                     env.start(xid3, XAResource.TMNOFLAGS);
                     db.put(null, key, value);
                     env.end(xid3, XAResource.TMSUCCESS);
                     // do work for branch 4
                     env.start(xid4, XAResource.TMNOFLAGS);
                     db.put(null, key, value);
                     env.end(xid4, XAResource.TMSUCCESS);
                     // prepare branches 1 through 4
                     int r = env.prepare(xid1);
                     if(r != XAResource.XA_OK) System.out.println("Failed to prepare xid1");
                     r = env.prepare(xid2);
                     if(r != XAResource.XA_OK) System.out.println("Failed to prepare xid2");
                     r = env.prepare(xid3);
                     if(r != XAResource.XA_OK) System.out.println("Failed to prepare xid3");
                     r = env.prepare(xid4);
                     if(r != XAResource.XA_OK) System.out.println("Failed to prepare xid4");
                     env.commit(xid1, false); // commit branch 1
                     env.rollback(xid2); // rollback branch 2

                     // We don't close the db or env to simulate a crash with branches 3 and 4 prepared
                } catch (DatabaseException x){
                } catch (XAException e) {
           public void runSecond(){
                System.out.println("starting runSecond()");
                try {
                     Xid[] xids = env.recover(XAResource.TMNOFLAGS);
                     DatabaseEntry key = new DatabaseEntry();
                     DatabaseEntry value = new DatabaseEntry();
                     for(Xid xid : xids){
                          XidImpl xidImpl = new XidImpl(xid);
                          System.out.println("Recovering: " + xidImpl);
                          env.start(xid, XAResource.TMJOIN);
                          OperationStatus status = db.get(null, key, value, LockMode.DEFAULT);
                          if(status != OperationStatus.SUCCESS){
                               System.out.println("Failed to find data for xid " + xidImpl);
                          } else {
                               System.out.println("Recovered data: " + toString(value.getData()));
                          env.end(xid, XAResource.TMSUCCESS);
                // We don't close the db or env to simulate a crash
                } catch (DatabaseException x){
                } catch (XAException e) {
           public static class XidImpl implements Xid {

                private String globalTransactionId;
                private String branchQualifier;
                private int formatId;
                public XidImpl(Xid xid) {
                     this.globalTransactionId = TestBerkeleyDBRecovery.toString(xid.getGlobalTransactionId());
                     this.branchQualifier = TestBerkeleyDBRecovery.toString(xid.getBranchQualifier());
                     this.formatId = xid.getFormatId();
                public XidImpl(String globalTransactionId, String branchQualifier, int formatId) {
                     this.globalTransactionId = globalTransactionId;
                     this.branchQualifier = branchQualifier;
                     this.formatId = formatId;

                public byte[] getBranchQualifier() {
                     return TestBerkeleyDBRecovery.fromString(branchQualifier);

                public int getFormatId() {
                     return formatId;

                public byte[] getGlobalTransactionId() {
                     return TestBerkeleyDBRecovery.fromString(globalTransactionId);
                public String toString(){
                     StringBuilder buf = new StringBuilder();
                     buf.append(", branchQualifier=").append(branchQualifier);
                     buf.append(", formatId=").append(formatId).append('}');
                     return buf.toString();

           public static String toString(byte[] ba){
                try {
                     return new String(ba, "UTF-8");
                } catch (UnsupportedEncodingException e) {
                     throw new RuntimeException(e);
           public static byte[] fromString(String s){
                try {
                     return s.getBytes("UTF-8");
                } catch (UnsupportedEncodingException e) {
                     throw new RuntimeException(e);
           public static void main(String[] args) {
                File envHomeDir = new File("C:/test/bdb");
                try {
                     File dataFile = new File(envHomeDir, "00000000.jdb");
                     boolean newEnvironment = !dataFile.exists();
                     TestBerkeleyDBRecovery test = new TestBerkeleyDBRecovery(envHomeDir);
                     if(newEnvironment) {
                     } else {
                } catch (DatabaseException e) {