Thread: XA recovery loses transactions


Permlink Replies: 2 - Pages: 1 - Last Post: Nov 24, 2007 11:51 AM Last Post By: Charles Lamb
jamespharaoh

Posts: 1
Registered: 11/19/07
XA recovery loses transactions
Posted: Nov 19, 2007 12:51 AM
Click to report abuse...   Click to reply to this thread Reply
Hi,

I am getting some unexpected behaviour when testing the distributed
resource recovery functionality in Java Edition.

As I understand it, a prepared but not committed or rolledback
transaction should stay in the recovery list. The X/Open standard
says:

"Two consecutive complete recovery scans return the same list of
transaction branches unless a transaction manager calls xa_commit(),
xa_forget(), xa_prepare(), or xa_rollback() for that resource manager,
or unless that resource manager heuristically completes some branches,
between the two recovery scans."

I don't really understand the heuristic part, so perhaps that is my
prolem, but as I read it a prepared transaction should come up again
and again until I do something with it.

Please see the attached test program. This contains code to prepare
but not complete a transaction, and code to count the number of
transactions pending recover. This gives me the following results:

$ java -cp .:je-3.2.44.jar XATest prepare ~/dbtest
$ java -cp .:je-3.2.44.jar XATest recover ~/dbtest
1
$ java -cp .:je-3.2.44.jar XATest recover ~/dbtest
1
$ java -cp .:je-3.2.44.jar XATest recover ~/dbtest
1
$ java -cp .:je-3.2.44.jar XATest recover ~/dbtest
0

So on the fourth call, I am getting no transactions to recover. The
zero result is repeated on further attempts. Surely this is not
correct behaviour? Any insight would be appreciated.

My main concern here is that a transaction has been lost somewhere in
the system which spoils the atomicity in a distributed system.

James

---- XATest.java:

import com.sleepycat.je.Database;

public class XATest {

private static String path;

public static Xid xid (final int format, final String global, final String branch) {
return new Xid () {

public byte[] getBranchQualifier () {
try { return branch.getBytes ("utf-8"); }
catch (UnsupportedEncodingException e) { throw new RuntimeException (e); }
}

public int getFormatId () {
return format;
}

public byte[] getGlobalTransactionId () {
try { return global.getBytes ("utf-8"); }
catch (UnsupportedEncodingException e) { throw new RuntimeException (e); }
}

@Override
public String toString () {
return global + ":" + branch;
}

@Override
public boolean equals (Object other) {
if (! (other instanceof Xid)) return false;
Xid xid = (Xid) other;
try {
return format == xid.getFormatId ()
&& global.getBytes ("utf-8") == xid.getGlobalTransactionId ()
&& branch.getBytes ("utf-8") == xid.getBranchQualifier ();
} catch (UnsupportedEncodingException e) { throw new RuntimeException (e); }
}
};
}

public static void prepare () throws Exception {

EnvironmentConfig envConfig = new EnvironmentConfig ();
envConfig.setTransactional (true);
envConfig.setAllowCreate (true);
XAEnvironment env = new XAEnvironment (new File (path), envConfig);

DatabaseConfig dbConfig = new DatabaseConfig ();
dbConfig.setTransactional (true);
dbConfig.setAllowCreate (true);
Database db = env.openDatabase (null, "test", dbConfig);

Xid xid = xid (0, "a", "b");

env.start (xid, XAResource.TMNOFLAGS);

DatabaseEntry keyEntry = new DatabaseEntry ("key".getBytes ("utf-8"));
DatabaseEntry valEntry = new DatabaseEntry ("val".getBytes ("utf-8"));
db.put (null, keyEntry, valEntry);

env.end (xid, XAResource.TMSUCCESS);

env.prepare (xid);
}

public static void recover () throws Exception {

EnvironmentConfig envConfig = new EnvironmentConfig ();
envConfig.setTransactional (true);
envConfig.setAllowCreate (true);
XAEnvironment env = new XAEnvironment (new File (path), envConfig);

Xid[] xids = env.recover (XAResource.TMNOFLAGS);
System.out.println (xids.length);
}

public static void main (String[] args) {
try {
path = args [1];
if (args [0].equals ("prepare"))
prepare ();
if (args [0].equals ("recover"))
recover ();
} catch (Exception e) {
throw e instanceof RuntimeException? (RuntimeException) e : new RuntimeException (e);
}
}
}

Charles Lamb

Posts: 454
Registered: 07/30/06
Re: XA recovery loses transactions
Posted: Nov 19, 2007 6:24 PM   in response to: jamespharaoh in response to: jamespharaoh
Click to report abuse...   Click to reply to this thread Reply
Hi James,

Thanks for bringing this to our attention. You've found a bug in JE. I'll come up with a fix for you. Is this holding you up?

Regards,

Charles Lamb
Charles Lamb

Posts: 454
Registered: 07/30/06
Re: XA recovery loses transactions
Posted: Nov 24, 2007 11:51 AM   in response to: Charles Lamb in response to: Charles Lamb
Click to report abuse...   Click to reply to this thread Reply
Hi James,

We have a fix for this and it will be in the next release of JE. If you would like a patch for it sooner, please let me know and I'll send it off to you.

Regards,

Charles Lamb
Legend
Guru Guru : 2500 - 1000000 pts
Expert Expert : 1000 - 2499 pts
Pro Pro : 500 - 999 pts
Journeyman Journeyman : 200 - 499 pts
Newbie Newbie : 0 - 199 pts
Oracle ACE Director
Oracle ACE Member
Oracle Employee ACE
Helpful Answer (5 pts)
Correct Answer (10 pts)

Point your RSS reader here for a feed of the latest messages in all forums