@Test public void shouldHoldExclusiveIndexLockIfNodeDoesNotExist() throws Exception { // given NodeValueIndexCursor cursor = mock( NodeValueIndexCursor.class ); when( cursor.next() ).thenReturn( false, false ); when( cursor.nodeReference() ).thenReturn( -1L ); // when long nodeId = LockingNodeUniqueIndexSeek.apply( locks, LockTracer.NONE, () -> cursor, uniqueNodeIndexSeeker, read, index, predicate ); // then assertEquals( -1L, nodeId ); order.verify( locks ).acquireShared( LockTracer.NONE, INDEX_ENTRY, resourceId ); order.verify( locks ).releaseShared( INDEX_ENTRY, resourceId ); order.verify( locks ).acquireExclusive( LockTracer.NONE, INDEX_ENTRY, resourceId ); verifyNoMoreInteractions( locks ); verify( cursor ).close(); } }
@Test public void shouldHoldSharedIndexLockIfNodeIsConcurrentlyCreated() throws Exception { // given NodeValueIndexCursor cursor = mock( NodeValueIndexCursor.class ); when( cursor.next() ).thenReturn( false, true ); when( cursor.nodeReference() ).thenReturn( 42L ); // when long nodeId = LockingNodeUniqueIndexSeek.apply( locks, LockTracer.NONE, () -> cursor, uniqueNodeIndexSeeker, read, index, predicate ); // then assertEquals( 42L, nodeId ); order.verify( locks ).acquireShared( LockTracer.NONE, INDEX_ENTRY, resourceId ); order.verify( locks ).releaseShared( INDEX_ENTRY, resourceId ); order.verify( locks ).acquireExclusive( LockTracer.NONE, INDEX_ENTRY, resourceId ); order.verify( locks ).acquireShared( LockTracer.NONE, INDEX_ENTRY, resourceId ); order.verify( locks ).releaseExclusive( INDEX_ENTRY, resourceId ); verifyNoMoreInteractions( locks ); verify( cursor ).close(); }
@Test public void shouldTraceWaitTimeWhenTryingToAcquireExclusiveLockAndSharedIsHeld() throws Exception { // given Tracer tracerA = new Tracer(); Tracer tracerB = new Tracer(); clientA.acquireShared( tracerA, NODE, 17 ); // when Future<Object> future = acquireExclusive( clientB, tracerB, NODE, 17 ).callAndAssertWaiting(); // then clientA.releaseShared( NODE, 17 ); future.get(); tracerA.assertCalls( 0 ); tracerB.assertCalls( 1 ); }
@Test public void shouldUpgradeAndDowngradeSameSharedLock() { // when clientA.acquireShared( LockTracer.NONE, NODE, 1L ); clientB.acquireShared( LockTracer.NONE, NODE, 1L ); LockIdentityExplorer sharedLockExplorer = new LockIdentityExplorer( NODE, 1L ); locks.accept( sharedLockExplorer ); // then xclusive should wait for shared from other client to be released Future<Object> exclusiveLockFuture = acquireExclusive( clientB, LockTracer.NONE, NODE, 1L ).callAndAssertWaiting(); // and when clientA.releaseShared( NODE, 1L ); // exclusive lock should be received assertNotWaiting( clientB, exclusiveLockFuture ); // and when releasing exclusive clientB.releaseExclusive( NODE, 1L ); // we still should have same read lock LockIdentityExplorer releasedLockExplorer = new LockIdentityExplorer( NODE, 1L ); locks.accept( releasedLockExplorer ); // we still hold same lock as before assertEquals( sharedLockExplorer.getLockIdentityHashCode(), releasedLockExplorer.getLockIdentityHashCode() ); }
@Test public void sharedLocksShouldStack() { // When clientA.acquireShared( LockTracer.NONE, NODE, 1L ); clientA.acquireShared( LockTracer.NONE, NODE, 1L ); clientA.acquireShared( LockTracer.NONE, NODE, 1L ); // Then exclusive locks should wait Future<Object> clientBLock = acquireExclusive( clientB, LockTracer.NONE, NODE, 1L ).callAndAssertWaiting(); // And when clientA.releaseShared( NODE, 1L ); clientA.releaseShared( NODE, 1L ); // Then other thread should still wait assertWaiting( clientB, clientBLock ); // But when clientA.releaseShared( NODE, 1L ); // Then assertNotWaiting( clientB, clientBLock ); }
@Test public void exclusiveLocksShouldBeReentrantAndBlockOtherSharedLocks() { // When clientA.acquireExclusive( LockTracer.NONE, NODE, 1L ); clientA.acquireShared( LockTracer.NONE, NODE, 1L ); clientA.tryExclusiveLock( NODE, 1L ); // Then exclusive locks should wait Future<Object> clientBLock = acquireShared( clientB, LockTracer.NONE, NODE, 1L ).callAndAssertWaiting(); // And when clientA.releaseExclusive( NODE, 1L ); clientA.releaseShared( NODE, 1L ); // Then other thread should still wait assertWaiting( clientB, clientBLock ); // But when clientA.releaseExclusive( NODE, 1L ); // Then assertNotWaiting( clientB, clientBLock ); }
@Test public void shouldRetainSharedLockWhenAcquiredAfterExclusiveLock() { // When clientA.acquireExclusive( LockTracer.NONE, NODE, 1L ); clientA.acquireShared( LockTracer.NONE, NODE, 1L ); // Then this should wait Future<Object> clientBLock = acquireExclusive( clientB, LockTracer.NONE, NODE, 1L ).callAndAssertWaiting(); // And when clientA.releaseExclusive( NODE, 1L ); // Then other thread should still wait assertWaiting( clientB, clientBLock ); // But when clientA.releaseShared( NODE, 1L ); // Then assertNotWaiting( clientB, clientBLock ); }
@Test public void releaseMultipleSharedLocksWhileHavingSomeExclusiveLocks() { clientA.acquireExclusive( LockTracer.NONE, NODE, 10, 100, 1000 ); clientA.acquireShared( LockTracer.NONE, NODE, 100, 1000, 10000 ); assertFalse( clientB.trySharedLock( NODE, 10 ) ); assertFalse( clientB.trySharedLock( NODE, 100 ) ); assertFalse( clientB.trySharedLock( NODE, 1000 ) ); assertFalse( clientB.tryExclusiveLock( NODE, 10000 ) ); assertEquals( 4, lockCount() ); clientA.releaseShared( NODE, 100, 1000 ); assertFalse( clientB.trySharedLock( NODE, 10 ) ); assertFalse( clientB.trySharedLock( NODE, 100 ) ); assertFalse( clientB.trySharedLock( NODE, 1000 ) ); assertFalse( clientB.tryExclusiveLock( NODE, 10000 ) ); assertEquals( 4, lockCount() ); }
@Test public void shouldAcquireExclusiveIfClientIsOnlyOneHoldingShared() { // When clientA.acquireShared( LockTracer.NONE, NODE, 1L ); clientA.acquireExclusive( LockTracer.NONE, NODE, 1L ); // Then shared locks should wait Future<Object> clientBLock = acquireExclusive( clientB, LockTracer.NONE, NODE, 1L ).callAndAssertWaiting(); // And when clientA.releaseExclusive( NODE, 1L ); // Then other thread should still wait assertWaiting( clientB, clientBLock ); // But when clientA.releaseShared( NODE, 1L ); // Then assertNotWaiting( clientB, clientBLock ); }
@Test public void releaseSharedLocksAcquiredSeparately() { clientA.acquireShared( LockTracer.NONE, NODE, 1 ); clientA.acquireShared( LockTracer.NONE, NODE, 2 ); clientA.acquireShared( LockTracer.NONE, NODE, 3 ); assertEquals( 3, lockCount() ); assertFalse( clientB.tryExclusiveLock( NODE, 1 ) ); assertFalse( clientB.tryExclusiveLock( NODE, 2 ) ); assertFalse( clientB.tryExclusiveLock( NODE, 3 ) ); clientA.releaseShared( NODE, 1, 2, 3 ); assertEquals( 0, lockCount() ); assertTrue( clientB.tryExclusiveLock( NODE, 1 ) ); assertTrue( clientB.tryExclusiveLock( NODE, 2 ) ); assertTrue( clientB.tryExclusiveLock( NODE, 3 ) ); }
@Test public void sharedLocksShouldNotReplaceExclusiveLocks() { // When clientA.acquireExclusive( LockTracer.NONE, NODE, 1L ); clientA.acquireShared( LockTracer.NONE, NODE, 1L ); // Then shared locks should wait Future<Object> clientBLock = acquireShared( clientB, LockTracer.NONE, NODE, 1L ).callAndAssertWaiting(); // And when clientA.releaseShared( NODE, 1L ); // Then other thread should still wait assertWaiting( clientB, clientBLock ); // But when clientA.releaseExclusive( NODE, 1L ); // Then assertNotWaiting( clientB, clientBLock ); }
@Test public void shouldRetainExclusiveLockAfterReleasingSharedLock() { // When clientA.acquireShared( LockTracer.NONE, NODE, 1L ); clientA.acquireExclusive( LockTracer.NONE, NODE, 1L ); // Then shared locks should wait Future<Object> clientBLock = acquireShared( clientB, LockTracer.NONE, NODE, 1L ).callAndAssertWaiting(); // And when clientA.releaseShared( NODE, 1L ); // Then other thread should still wait assertWaiting( clientB, clientBLock ); // But when clientA.releaseExclusive( NODE, 1L ); // Then assertNotWaiting( clientB, clientBLock ); }
@Test public void exclusiveShouldWaitForShared() { // When clientA.acquireShared( LockTracer.NONE, NODE, 1L ); // Then other shared locks are allowed clientC.acquireShared( LockTracer.NONE, NODE, 1L ); // But exclusive locks should wait Future<Object> clientBLock = acquireExclusive( clientB, LockTracer.NONE, NODE, 1L ).callAndAssertWaiting(); // And when clientA.releaseShared( NODE, 1L ); clientC.releaseShared( NODE, 1L ); // Then this should not block assertNotWaiting( clientB, clientBLock ); }
@Test public void releaseMultipleSharedLocks() { clientA.acquireShared( LockTracer.NONE, NODE, 10, 100, 1000 ); assertEquals( 3, lockCount() ); clientA.releaseShared( NODE, 100, 1000 ); assertEquals( 1, lockCount() ); assertFalse( clientB.tryExclusiveLock( NODE, 10 ) ); assertTrue( clientB.tryExclusiveLock( NODE, 100 ) ); assertTrue( clientB.tryExclusiveLock( NODE, 1000 ) ); }
void release() { if ( shared ) { client.releaseShared( resourceType, resourceId ); } else { client.releaseExclusive( resourceType, resourceId ); } } }
private void releaseSharedLock( ResourceTypes types, long... ids ) { ktx.statementLocks().pessimistic().releaseShared( types, ids ); }
@Test public void releaseMultipleAlreadyAcquiredSharedLocks() { clientA.acquireShared( LockTracer.NONE, NODE, 10, 100, 1000 ); clientA.acquireShared( LockTracer.NONE, NODE, 100, 1000, 10000 ); clientA.releaseShared( NODE, 100, 1000 ); assertEquals( 4, lockCount() ); assertFalse( clientB.tryExclusiveLock( NODE, 100 ) ); assertFalse( clientB.tryExclusiveLock( NODE, 1000 ) ); clientA.releaseShared( NODE, 100, 1000 ); assertEquals( 2, lockCount() ); }
@Test public void shouldReleaseSharedLocksAcquiredInABatch() { clientA.acquireShared( LockTracer.NONE, NODE, 1, 10, 100 ); assertEquals( 3, lockCount() ); clientA.releaseShared( NODE, 1 ); assertEquals( 2, lockCount() ); clientA.releaseShared( NODE, 10 ); assertEquals( 1, lockCount() ); clientA.releaseShared( NODE, 100 ); assertEquals( 0, lockCount() ); }
@Test( expected = LockClientStoppedException.class ) public void releaseSharedThrowsWhenClientStopped() { stoppedClient().releaseShared( NODE, 1 ); }
@Override protected void acquireLock( LockWorkerState state ) { state.doing( "-R " + resource ); state.client.releaseShared( NODE, resource ); state.done(); } }, true );