-
30. Re: Transactions: atomicity OK, isolation KO
jason.greene Nov 3, 2009 12:05 PM (in response to chtimi2)To use the dommy transaction manager in the easiest possible way. Just do getRuntimeConfig().setTransactionManagerLookupClass("org.jboss.cache.transaction.DummyTransactionManagerLookup");
Then instead of using UserTransaction, you can just use JTA directly:
tm = getRuntimeCoinfig.getTransactionManager();
tm.begin();
// blah
tm.commit(); -
31. Re: Transactions: atomicity OK, isolation KO
chtimi2 Nov 3, 2009 12:56 PM (in response to chtimi2)Thanks, i didn't think of using a TM instead of a UserTransaction.
Using that, i get exactly the same behaviour as with Atomikos though:
-with pessimistic locking scheme, the test passes
-with mvcc, i get the same error:Detected write skew on Fqn [/foo/mynode]. Another process has changed the node since we last read it!
-
32. Re: Transactions: atomicity OK, isolation KO
jason.greene Nov 3, 2009 1:21 PM (in response to chtimi2)That's strange. You should not be getting write-skew, since you never read before you acquire a lock on the node. What happens if you disable write-skew detection?
-
33. Re: Transactions: atomicity OK, isolation KO
chtimi2 Nov 4, 2009 3:25 AM (in response to chtimi2)If i comment the write-skew check, i also get the same behaviour as i did with Atomikos: no exception, but the test fails with
java.lang.AssertionError: expected:<10> but was:<2>
That is, it fails exactly the same way as if my threads were writing concurrently to a non-thread-safe object. -
34. Re: Transactions: atomicity OK, isolation KO
chtimi2 Nov 4, 2009 4:01 AM (in response to chtimi2)Good news! It works at last, both with the default TM and with Atomikos.
To make it work, i used the conf:<locking isolationLevel="READ_COMMITTED" lockParentForChildInsertRemove="false" lockAcquisitionTimeout="20000" nodeLockingScheme="mvcc" writeSkewCheck="false" concurrencyLevel="500"/>
Whereas the not working test had this block commented.
What's strange is that i had used the same conf a while ago and it failed, but at the time i was using the old configuration file format (with <mbean tags>). It is not the first time i had a bug that was corrected by using the new configuration file.
Since this took so long, i'm going to post the complete test and configuration in the default and Atomikos cases. -
35. Re: Transactions: atomicity OK, isolation KO
chtimi2 Nov 4, 2009 4:06 AM (in response to chtimi2)With JBC's default TM
The test:public class ComportementTransactionnelTableCoreCache { private Cache cache; protected TracksTable table = new TracksTableImpl (); protected final ExecutorService executorService = Executors.newCachedThreadPool(); @Test public void isolation_RW_RW () throws Exception { table.initScalar (); int nbWriters = 10; CountDownLatch latch = new CountDownLatch ( nbWriters ); for ( int i = 0 ; i < nbWriters ; i ++ ) { executorService.submit ( new AsyncIncrement ( latch ) ); } latch.await (); assertEquals ( nbWriters , table.getScalar() ); } protected class AsyncIncrement implements Runnable { private final CountDownLatch latch; public AsyncIncrement ( CountDownLatch latch ) { this.latch = latch; } @Override public void run() { try { table.incrementScalar (); } finally { latch.countDown (); } } } @Before public void before () { cache = initPojoCache (); cache.start(); table.setCache ( cache ); table.initScalar(); assertEquals ( 0 , table.getScalar() ); } @After public void after () { table.unsetCache (); cache.stop(); } private Cache initPojoCache() { CacheFactory factory = new DefaultCacheFactory(); Cache cache = factory.createCache("resources/META-INF/replSync-service.xml", false); //cache.getConfiguration().setWriteSkewCheck(true); cache.create(); return cache; } }
The resource under test:public class TracksTableImpl implements TracksTable { private Cache cache; private int scalar; @Override public void incrementScalar() { try { TransactionManager tx = getTm (); tx.begin(); cache.put("/foo/mynode", "_lockthisplease_", "_lockthisplease_"); cache.getInvocationContext().getOptionOverrides().setForceWriteLock(true); int tmp = (Integer)cache.get("/foo/mynode", "scalar"); tmp++; cache.put ( "/foo/mynode" , "scalar" , tmp ); tx.commit(); } catch ( Exception e ) { e.printStackTrace(); throw new RuntimeException (); } } @Override public int getScalar() { try { TransactionManager tx = getTm (); tx.begin(); int ret = (Integer)cache.get("/foo/mynode", "scalar"); tx.commit(); return ret; } catch ( Exception e ) { e.printStackTrace(); throw new RuntimeException (); } } @Override public void initScalar() { try { TransactionManager tx = getTm (); tx.begin(); cache.put ( "/foo/mynode" , "scalar" , 0 ); tx.commit(); } catch ( Exception e ) { e.printStackTrace(); throw new RuntimeException (); } } @Override public void setCache ( Cache cache ) { this.cache = cache; } @Override public void unsetCache () { this.cache = null; } private TransactionManager getTm() { return cache.getConfiguration().getRuntimeConfig().getTransactionManager(); } }
The config:<?xml version="1.0" encoding="UTF-8"?> <jbosscache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:jboss:jbosscache-core:config:3.2"> <transaction transactionManagerLookupClass="org.jboss.cache.transaction.DummyTransactionManagerLookup"/> <!-- isolation levels supported: READ_COMMITTED and REPEATABLE_READ nodeLockingSchemes: mvcc, pessimistic (deprecated), optimistic (deprecated) --> <locking isolationLevel="READ_COMMITTED" lockParentForChildInsertRemove="false" lockAcquisitionTimeout="20000" nodeLockingScheme="mvcc" writeSkewCheck="false" concurrencyLevel="500"/> <!-- By not specifying the 'clustering' element, the cache runs in LOCAL mode. --> </jbosscache>
-
36. Re: Transactions: atomicity OK, isolation KO
chtimi2 Nov 4, 2009 4:10 AM (in response to chtimi2)With Atomikos
The test:public class ComportementTransactionnelTableCoreCache { private Cache cache; protected TracksTable table = new TracksTableImpl (); protected final ExecutorService executorService = Executors.newCachedThreadPool(); @Test public void isolation_RW_RW () throws Exception { table.initScalar (); int nbWriters = 10; CountDownLatch latch = new CountDownLatch ( nbWriters ); for ( int i = 0 ; i < nbWriters ; i ++ ) { executorService.submit ( new AsyncIncrement ( latch ) ); } latch.await (); assertEquals ( nbWriters , table.getScalar() ); } protected class AsyncIncrement implements Runnable { private final CountDownLatch latch; public AsyncIncrement ( CountDownLatch latch ) { this.latch = latch; } @Override public void run() { try { table.incrementScalar (); } finally { latch.countDown (); } } } @Before public void before () { cache = initPojoCache (); cache.start(); table.setCache ( cache ); table.initScalar(); assertEquals ( 0 , table.getScalar() ); } @After public void after () { table.unsetCache (); cache.stop(); } private Cache initPojoCache() { CacheFactory factory = new DefaultCacheFactory(); Cache cache = factory.createCache("resources/META-INF/replSync-service.xml", false); TransactionManager tm = new com.atomikos.icatch.jta.UserTransactionManager (); cache.getConfiguration().getRuntimeConfig().setTransactionManager(tm); cache.create(); return cache; } }
The resource under test:public class TracksTableImpl implements TracksTable { private Cache cache; private int scalar; @Override public void incrementScalar() { try { UserTransaction tx = getUt (); tx.begin(); cache.put("/foo/mynode", "_lockthisplease_", "_lockthisplease_"); cache.getInvocationContext().getOptionOverrides().setForceWriteLock(true); int tmp = (Integer)cache.get("/foo/mynode", "scalar"); tmp++; cache.put ( "/foo/mynode" , "scalar" , tmp ); tx.commit(); } catch ( Exception e ) { e.printStackTrace(); throw new RuntimeException (); } } @Override public int getScalar() { try { UserTransaction tx = getUt (); tx.begin(); int ret = (Integer)cache.get("/foo/mynode", "scalar"); tx.commit(); return ret; } catch ( Exception e ) { e.printStackTrace(); throw new RuntimeException (); } } @Override public void initScalar() { try { UserTransaction tx = getUt (); tx.begin(); cache.put ( "/foo/mynode" , "scalar" , 0 ); tx.commit(); } catch ( Exception e ) { e.printStackTrace(); throw new RuntimeException (); } } @Override public void setCache ( Cache cache ) { this.cache = cache; } @Override public void unsetCache () { this.cache = null; } private UserTransaction getUt() { return new com.atomikos.icatch.jta.UserTransactionImp(); } }
The config:<?xml version="1.0" encoding="UTF-8"?> <jbosscache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:jboss:jbosscache-core:config:3.2"> <!-- isolation levels supported: READ_COMMITTED and REPEATABLE_READ nodeLockingSchemes: mvcc, pessimistic (deprecated), optimistic (deprecated) --> <locking isolationLevel="READ_COMMITTED" lockParentForChildInsertRemove="false" lockAcquisitionTimeout="20000" nodeLockingScheme="mvcc" writeSkewCheck="false" concurrencyLevel="500"/> <!-- By not specifying the 'clustering' element, the cache runs in LOCAL mode. --> </jbosscache>
-
37. Re: Transactions: atomicity OK, isolation KO
chtimi2 Nov 4, 2009 4:16 AM (in response to chtimi2)With REPEATABLE_READ (the default), it still fails in the way i posted earlier:
-if writeSkewCheck is true, i get a write skew exception
-otherwise, no exceptions but the behaviour is that of threads writing to a non-thread-safe object -
38. Re: Transactions: atomicity OK, isolation KO
chtimi2 Nov 4, 2009 12:30 PM (in response to chtimi2)Also, would it be possible to have a cache setup parameter that does the same as
cache.getCache().getInvocationContext().getOptionOverrides().setForceWriteLock(true);
without needing to call it in every service method? Sure it isn't always necessary but in first approximation this would be helpful (optimize later). -
39. Re: Transactions: atomicity OK, isolation KO
galder.zamarreno Nov 5, 2009 1:14 PM (in response to chtimi2)chtimi2, i'm looking into the test you provided
-
40. Re: Transactions: atomicity OK, isolation KO
galder.zamarreno Nov 5, 2009 3:29 PM (in response to chtimi2)"chtimi2" wrote:
Also, would it be possible to have a cache setup parameter that does the same ascache.getCache().getInvocationContext().getOptionOverrides().setForceWriteLock(true);
without needing to call it in every service method? Sure it isn't always necessary but in first approximation this would be helpful (optimize later).
You can always provide an abstraction such as getWithWL(Fqn, Object) that does it for you :) -
41. Re: Transactions: atomicity OK, isolation KO
galder.zamarreno Nov 5, 2009 4:18 PM (in response to chtimi2)"chtimi2" wrote:
With REPEATABLE_READ (the default), it still fails in the way i posted earlier:
-if writeSkewCheck is true, i get a write skew exception
-otherwise, no exceptions but the behaviour is that of threads writing to a non-thread-safe object
I've just run your test with the dummy cache manager, and I randomly get a:org.jboss.cache.optimistic.DataVersioningException: Detected write skew on Fqn [/foo/mynode]. Another process has changed the node since we last read it! at org.jboss.cache.mvcc.RepeatableReadNode.markForUpdate(RepeatableReadNode.java:68) ...
I'm looking into that.
Wrt the behaivour when writeSkewCheck is false, could you please enhance your test so that you can assert it in such way that it fails under this condition? -
42. Re: Transactions: atomicity OK, isolation KO
galder.zamarreno Nov 5, 2009 4:23 PM (in response to chtimi2)chtimi2, please find a better version of the test in http://pastebin.mozilla.org/681497
It uses correct transaction usage patterns as instructed http://www.jboss.org/community/wiki/WhatIsTheCorrectPatternForUserTransactions, uses Callable and Future to propagate exceptions back to the main thread, and finally uses CyclicBarrier to make sure all threads start at the same time and they wait until everyone has finished.
I'm still investigating the write skew true issue. -
43. Re: Transactions: atomicity OK, isolation KO
galder.zamarreno Nov 9, 2009 3:21 AM (in response to chtimi2)Here's a bug report for the writeSkew issuel: https://jira.jboss.org/jira/browse/JBCACHE-1555
-
44. Re: Transactions: atomicity OK, isolation KO
chtimi2 Nov 10, 2009 4:17 AM (in response to chtimi2)Thanks for investigating the issue. I have a lot of slides to write so it might take a week before i can dig more but after that I will try your test, promised :)