Are lock striping and Tx compatible?
nfilotto Mar 15, 2011 3:55 AMHi all,
There is something that I cannot understand so far, I hope that someone can help me to understand. In order to reduce the foot print, I would like to set the parameter "useLockStriping" to "true", unfortunatelly it looks like it is nearly impossible to use it when we apply changes to our ISPN cache instance within a Tx without getting unpredictable deadlocks (aka TimeoutException "Unable to acquire lock after [xxx] on key [yyyy] for requestor [zzzz]! Lock held by [wwww]")
When we don't use the lock striping, we know that if we don't sort the keys, we can face deadlocks, for example if the first thread does:
tm.begin() // We acquire the lock on "A" cache.put("A","A"); // We acquire the lock on "B" cache.put("B","B"); // Here all the acquired locks will be released tm.commit();
Then in second thread, it does:
tm.begin() // We acquire the lock on "B" cache.put("B","B"); // We acquire the lock on "A" cache.put("A","A"); // Here all the acquired locks will be released tm.commit();
We will then create a deadlock, to fix this issue we have to sort the keys within the Tx which can easily be done.
In case of lock stripping it seems that we have no way to predict and avoid the deadlocks since the way the application get the locks is implementation-dependent so it cannot be predicted by definition. For example this following TestCase fails:
public class TestDeadlock extends TestCase { /** * We get an unpredictable deadlock because apart if we know the implementation * of the ReentrantStripedLockContainer, we cannot know that the keys * 8 * "a" and 13 * "a" are mapped to the same lock and 11 * "a" and 14 * "a" are * both mapped to the same lock that is different from the first one. * In the use case below the keys are sorted but we still get a deadlock */ public void testUseLockStripingTrueUnpredictableDeadLock() throws Exception { DefaultCacheManager manager = new DefaultCacheManager(); manager.getDefaultConfiguration().setInvocationBatchingEnabled(true); manager.getDefaultConfiguration().setLockAcquisitionTimeout(1000); final Cache<String, String> cache = manager.getCache(); final CountDownLatch startSignal = new CountDownLatch(1); final CountDownLatch endSignal = new CountDownLatch(2); final AtomicReference<Exception> exception = new AtomicReference<Exception>(); new Thread() { /** * @see java.lang.Thread#run() */ @Override public void run() { try { cache.startBatch(); startSignal.await(); // 8 * "a" cache.put("aaaaaaaa", "Value"); // 14 * "a" cache.put("aaaaaaaaaaaaaa", "Value"); cache.endBatch(true); } catch (Exception e) { exception.set(e); } finally { endSignal.countDown(); } } }.start(); new Thread() { /** * @see java.lang.Thread#run() */ @Override public void run() { try { cache.startBatch(); startSignal.await(); // 11 * "a" cache.put("aaaaaaaaaaa", "Value2"); // 13 * "a" cache.put("aaaaaaaaaaaaa", "Value2"); cache.endBatch(true); } catch (Exception e) { exception.set(e); } finally { endSignal.countDown(); } } }.start(); startSignal.countDown(); endSignal.await(); assertNull(exception.get()); } }
My question is: Can you confirm that lock stripping and Tx are not compatible? If not what is the trick to use?
Thank you in advance for your answers,
BR,
Nicolas
The use case was incorrect