@Test public void testReentrantLock() { long start = System.currentTimeMillis(); String asset = "p1"; // make sure the lock is created first PLock pc = pessimisticLockManager.findOrPersistPLock(asset); assertNotNull(pc); dbInstance.closeSession(); // test double acquisition within same transaction PLock pc1 = pessimisticLockManager.findOrPersistPLock(asset); assertNotNull(pc1); PLock pc2 = pessimisticLockManager.findOrPersistPLock(asset); assertNotNull(pc2); dbInstance.closeSession(); // and without explicit transaction boundary. PLock p1 = pessimisticLockManager.findOrPersistPLock(asset); assertNotNull(p1); PLock p2 = pessimisticLockManager.findOrPersistPLock(asset); assertNotNull(p2); long stop = System.currentTimeMillis(); long diff = stop - start; assertTrue( "5 select's took longer than 10 seconds -> deadlock / lock timeout ? dur in ms was:" + diff, diff < 10000); }
@Test public void testNestedLockingSupported() { log.info("testing if nested locking is supported"); // make sure all three row entries for the locks are created, otherwise the system-wide locking // applied on lock-row-creation cannot support row-level-locking by definition. PLock pc1 = pessimisticLockManager.findOrPersistPLock("blabla"); assertNotNull(pc1); PLock pc2 = pessimisticLockManager.findOrPersistPLock("blublu"); assertNotNull(pc2); PLock pc3 = pessimisticLockManager.findOrPersistPLock("blibli"); assertNotNull(pc3); dbInstance.closeSession(); final List<Long> holder = new ArrayList<Long>(1); // first thread acquires the two locks and waits and continues holding the lock for some time. PLock p1 = pessimisticLockManager.findOrPersistPLock("blabla"); assertNotNull(p1); PLock p3 = pessimisticLockManager.findOrPersistPLock("blibli"); assertNotNull(p3); new Thread( new Runnable() { public void run() { PLock p2 = pessimisticLockManager.findOrPersistPLock("blibli"); assertNotNull(p2); long p2Acquired = System.nanoTime(); holder.add(new Long(p2Acquired)); dbInstance.closeSession(); } }) .start(); sleep(500); boolean acOk = holder.size() == 0; // the commit will drop the lock on blibli d dbInstance.closeSession(); sleep(500); boolean acNowOk = holder.size() == 1; // if row locking is not supported, then the timestamp when p2 has been acquired will be shortly // -after- p1 has been released assertTrue("since holding the blabla lock, no other may acquire it", acOk); assertTrue( "after having released the blabla lock, a next waiting thread must have acquired it after some time", acNowOk); }
@Test public void testSingleRowLockingSupported() { log.info( "testing if one lock only locks the given row and not the complete table (test whether the database supports rowlocking)"); // make sure both row entries for the locks are created, otherwise the system-wide locking // applied on lock-row-creation cannot support row-level-locking by definition. PLock pc1 = pessimisticLockManager.findOrPersistPLock("blabla"); Assert.assertNotNull(pc1); PLock pc2 = pessimisticLockManager.findOrPersistPLock("blublu"); Assert.assertNotNull(pc2); dbInstance.closeSession(); final List<Long> holder = new ArrayList<Long>(1); // first thread acquires the lock and waits and continues holding the lock for some time. PLock p1 = pessimisticLockManager.findOrPersistPLock("blabla"); Assert.assertNotNull(p1); new Thread( new Runnable() { public void run() { PLock p2 = pessimisticLockManager.findOrPersistPLock("blublu"); assertNotNull(p2); long p2Acquired = System.nanoTime(); holder.add(new Long(p2Acquired)); dbInstance.closeSession(); } }) .start(); sleep(500); long p1AboutToRelease = System.nanoTime(); dbInstance.closeSession(); // if row locking is not supported, then the timestamp when p2 has been acquired will be shortly // -after- p1 has been released boolean singleRowLockingOk = holder.size() > 0 && holder.get(0).longValue() < p1AboutToRelease; assertTrue( "the database does not seem to support row locking when executing 'select for update', critical for performance!, ", singleRowLockingOk); }
/** T1 T2 */ @Test public void testReentrantLock2Threads() { final String asset = "p1-2"; // make sure the lock is created first PLock pc = pessimisticLockManager.findOrPersistPLock(asset); assertNotNull(pc); dbInstance.closeSession(); final List<Exception> exceptionHolder = Collections.synchronizedList(new ArrayList<Exception>(1)); final CountDownLatch finishCount = new CountDownLatch(2); // thread 1 new Thread( new Runnable() { public void run() { try { PLock pc1 = pessimisticLockManager.findOrPersistPLock(asset); assertNotNull(pc1); log.info("Thread-1: got PLock pc1=" + pc1); log.info("Thread-1: sleep 1sec"); sleep(1000); PLock pc2 = pessimisticLockManager.findOrPersistPLock(asset); assertNotNull(pc2); log.info("Thread-1: got PLock pc2=" + pc2); log.info("Thread-1: finished"); } catch (Exception e) { exceptionHolder.add(e); } finally { finishCount.countDown(); try { dbInstance.commitAndCloseSession(); } catch (Exception e) { // ignore } } } }) .start(); // thread 2 new Thread( new Runnable() { public void run() { try { log.info("Thread-2: sleep 0.5sec"); sleep(500); log.info("Thread-2: try to get PLock..."); PLock p1 = pessimisticLockManager.findOrPersistPLock(asset); assertNotNull(p1); log.info("Thread-2: got PLock p1=" + p1); log.info("Thread-2: sleep 1sec"); sleep(1000); PLock p2 = pessimisticLockManager.findOrPersistPLock(asset); assertNotNull(p2); log.info("Thread-1: got PLock p2=" + p2); log.info("Thread-1: finished"); } catch (Exception e) { exceptionHolder.add(e); } finally { finishCount.countDown(); try { dbInstance.commitAndCloseSession(); } catch (Exception e) { // ignore } } } }) .start(); // sleep until t1 and t2 should have terminated/excepted try { finishCount.await(60, TimeUnit.SECONDS); } catch (InterruptedException e) { Assert.fail("Test takes too long (more than 60s)"); } // if not -> they are in deadlock and the db did not detect it for (Exception exception : exceptionHolder) { log.info("exception: " + exception.getMessage()); exception.printStackTrace(); } assertTrue("exception in test => see sysout", exceptionHolder.size() == 0); }
@Test public void testDeadLockTimeout() { log.info("testing if deadlock detection and handling is supported"); // make sure all three row entries for the locks are created, otherwise the system-wide locking // applied on lock-row-creation cannot support row-level-locking by definition. PLock pc1 = pessimisticLockManager.findOrPersistPLock("blabla"); assertNotNull(pc1); PLock pc2 = pessimisticLockManager.findOrPersistPLock("blublu"); assertNotNull(pc2); PLock pc3 = pessimisticLockManager.findOrPersistPLock("blibli"); assertNotNull(pc3); dbInstance.closeSession(); /** * t1 t2 bla bli .. .. .. .. .. .. bli .. .. .. bla -> deadlock! t2 waits on bla (already * acquired by t1, but t1 waits on bli, already acquired by t2) */ final List<Exception> exceptionHolder = Collections.synchronizedList(new ArrayList<Exception>(1)); final CountDownLatch finishCount = new CountDownLatch(2); // t1 new Thread( new Runnable() { public void run() { try { PLock p1 = pessimisticLockManager.findOrPersistPLock("blabla"); assertNotNull(p1); sleep(250); // now try to acquire blibli but that fails, since blibli is already locked by // thread 2. // but thread 2 cannot continue either, since it is waiting for lock blabla, which // is already hold by thread 1 // -> deadlock PLock p3 = pessimisticLockManager.findOrPersistPLock("blibli"); assertNotNull(p3); } catch (Exception e) { exceptionHolder.add(e); } finally { try { dbInstance.closeSession(); } catch (Exception e) { // ignore } finishCount.countDown(); } } }) .start(); // t2 new Thread( new Runnable() { public void run() { try { PLock p2 = pessimisticLockManager.findOrPersistPLock("blibli"); assertNotNull(p2); sleep(500); PLock p3 = pessimisticLockManager.findOrPersistPLock("blabla"); assertNotNull(p3); } catch (Exception e) { exceptionHolder.add(e); } finally { try { dbInstance.closeSession(); } catch (Exception e) { // ignore } finishCount.countDown(); } } }) .start(); // sleep until t1 and t2 should have terminated/excepted try { finishCount.await(8, TimeUnit.SECONDS); } catch (InterruptedException e) { Assert.fail("Takes too long (more than 8sec)"); } // if not -> they are in deadlock and the db did not detect it for (Exception exception : exceptionHolder) { log.error("exception: ", exception); } assertTrue("expected a deadlock exception, but got none", exceptionHolder.size() > 0); }
@Test public void testLockWaitTimout() { // Ignore Test if DB is PostgreSQL. PostgreSQL has not lock timeout assumeTrue(!isPostgresqlConfigured() && !isOracleConfigured()); final String asset = "testLockWaitTimout"; log.info("testing if holding a lock timeouts"); // make sure all three row entries for the locks are created, otherwise the system-wide locking // applied on lock-row-creation cannot support row-level-locking by definition. PLock pc3 = pessimisticLockManager.findOrPersistPLock("blibli"); assertNotNull(pc3); dbInstance.closeSession(); /** t1 t2 .. bli .. .. .. .. .. .. bli .. .. .. .... hold for longer than 30 secs */ final List<Exception> exceptionHolder = Collections.synchronizedList(new ArrayList<Exception>(1)); final CountDownLatch finishCount = new CountDownLatch(2); // t1 new Thread( new Runnable() { public void run() { try { sleep(500); PLock p3 = pessimisticLockManager.findOrPersistPLock(asset); assertNotNull(p3); } catch (Exception e) { exceptionHolder.add(e); } finally { finishCount.countDown(); try { dbInstance.closeSession(); } catch (Exception e) { // ignore } } } }) .start(); // t2 new Thread( new Runnable() { public void run() { try { PLock p2 = pessimisticLockManager.findOrPersistPLock(asset); assertNotNull(p2); sleep(55000); // holding the lock for more than the transaction timeout // (normally 30secs, configured where? hib) should cause a lock timeout // if the db is configured so (innodb_lock_wait_timeout). } catch (Exception e) { exceptionHolder.add(e); } finally { finishCount.countDown(); try { dbInstance.closeSession(); } catch (Exception e) { // ignore } } } }) .start(); // sleep until t1 and t2 should have terminated/excepted try { log.info("Sleep 55s"); finishCount.await(60, TimeUnit.SECONDS); } catch (InterruptedException e) { Assert.fail(""); } Assert.assertEquals( "expected a lock wait timeout exceeded exception", 1, exceptionHolder.size()); }