SERIALIZABLE Bug? Read of null, put, another thread reading
tallpsmith Nov 11, 2004 2:00 AMI'm not sure if this is a bug, or a feature I can't have! :)
Lets assume a local TreeCache with isolation Level set to Serializable.
Initiall when the cache is empty, the initial read will of course return null. Lets assume within this same transaction for this first thread that this thread then goes off to get the data from the DB. It holds a Read lock on the item at this point, I assume while it goes to get the data.
Mean while, another thread comes along and does the same thing. It starts a transaction, reads the value, and finds it null and will do the same thing... Hitting the db.
However what I would really like is for the second thread to block on the read until the first thread has finished. Basically I want any read or write to hold a write lock only on the ID node in question. Because the node in question is a logical ID of a row in a DB, it is better if the other thread waits for the first, because we only want 1 thread retrieving that value from the db.
Here's a test case method to show you what I mean:
public void testRaceConditionOnNotInCacheCondition() throws Exception{ UserTransaction tx = jotm.getUserTransaction(); tx.begin(); // we now read the null entry, and decide that we need to go do something. Object cachedObject = cache_.get("/SecurityInfo/", Integer.toString(23)); assertNull(cachedObject); // we expect this in this test /** * now start another Thread to go do the same action, looking for the value, but it SHOULD * see the result of the main thread put once it commits. */ Thread thread = new Thread(new Runnable() { public void run() { try { UserTransaction tx = jotm.getUserTransaction(); tx.begin(); log("OtherThread: inspecting the cache"); Object cachedObject = cache_.get("/SecurityInfo", Integer.toString(23)); log("OtherThread: read from cache:" + cachedObject); Thread.sleep(3000); cachedObject = cache_.get("/SecurityInfo", Integer.toString(23)); log("OtherThread: read(second time) from cache:" + cachedObject); /** * This should really fail because the other thread should actually have put something else there. */ cache_.put("/SecurityInfo", Integer.toString(23), "HelloWorldDIRTY!"); log("OtherThread: Has put something in the cache tha shouldn't be there"); tx.commit(); log("OtherThread: Committed... OH my."); } catch (Exception e) { e.printStackTrace(); } log("OthreThread: exiting"); }}); thread.start(); log("MainThread is now waiting a little bit"); Thread.sleep(2000); // wait long enough for the other thread to block a bit. Simulate a DB read log("MainThread is now putting something in the cache"); cache_.put("/SecurityInfo", Integer.toString(23), "HelloWorld"); Thread.sleep(2000); // wait long enough for the other thread to block a bit. Simulate a DB read tx.commit(); log("MainThread: committed"); thread.join(30000); log(cache_.get("/SecurityInfo", Integer.toString(23)).toString()); }
The output I get when running the above is:
MainThread is now waiting a little bit
OtherThread: inspecting the cache
OtherThread: read from cache:null
MainThread is now putting something in the cache
OtherThread: read(second time) from cache:HelloWorld
OtherThread: Has put something in the cache tha shouldn't be there
OtherThread: Committed... OH my.
OthreThread: exiting
MainThread: committed
HelloWorldDIRTY!
This to me should have failed at some level, because the 2nd thread clobbers the first threads put. I want the 2nd thread to block on the first read, and only then read the value the first thread put there.
Any ideas what's wrong here?