diff --git a/community/kernel/src/main/java/org/neo4j/kernel/impl/locking/DeferringLocks.java b/community/kernel/src/main/java/org/neo4j/kernel/impl/locking/DeferringLocks.java index 60fe69dcb8b37..430c99aa6455b 100644 --- a/community/kernel/src/main/java/org/neo4j/kernel/impl/locking/DeferringLocks.java +++ b/community/kernel/src/main/java/org/neo4j/kernel/impl/locking/DeferringLocks.java @@ -89,9 +89,12 @@ public DeferringLockClient( Client clientDelegate ) } @Override - public void acquireShared( ResourceType resourceType, long resourceId ) throws AcquireLockTimeoutException + public void acquireShared( ResourceType resourceType, long... resourceIds ) throws AcquireLockTimeoutException { - queueLock( resourceType, resourceId, shared ); + for ( long resourceId : resourceIds ) + { + queueLock( resourceType, resourceId, shared ); + } } private boolean queueLock( ResourceType resourceType, long resourceId, Set lockSet ) @@ -105,9 +108,12 @@ private boolean queueLock( ResourceType resourceType, long resourceId, Set localLocks = localShared( resourceType ); - LockResource resource = localLocks.get( resourceId ); - if ( resource != null ) - { - resource.acquireReference(); - } - else + for ( long resourceId : resourceIds ) { - resource = new LockResource( resourceType, resourceId ); - if ( manager.getReadLock( resource, lockTransaction ) ) + LockResource resource = localLocks.get( resourceId ); + if ( resource != null ) { - localLocks.put( resourceId, resource ); + resource.acquireReference(); } else { - throw new LockClientStoppedException( this ); + resource = new LockResource( resourceType, resourceId ); + if ( manager.getReadLock( resource, lockTransaction ) ) + { + localLocks.put( resourceId, resource ); + } + else + { + throw new LockClientStoppedException( this ); + } } } } @@ -93,27 +96,30 @@ public void acquireShared( Locks.ResourceType resourceType, long resourceId ) @Override - public void acquireExclusive( Locks.ResourceType resourceType, long resourceId ) + public void acquireExclusive( Locks.ResourceType resourceType, long... resourceIds ) { stateHolder.incrementActiveClients( this ); try { PrimitiveLongObjectMap localLocks = localExclusive( resourceType ); - LockResource resource = localLocks.get( resourceId ); - if ( resource != null ) - { - resource.acquireReference(); - } - else + for ( long resourceId : resourceIds ) { - resource = new LockResource( resourceType, resourceId ); - if ( manager.getWriteLock( resource, lockTransaction ) ) + LockResource resource = localLocks.get( resourceId ); + if ( resource != null ) { - localLocks.put( resourceId, resource ); + resource.acquireReference(); } else { - throw new LockClientStoppedException( this ); + resource = new LockResource( resourceType, resourceId ); + if ( manager.getWriteLock( resource, lockTransaction ) ) + { + localLocks.put( resourceId, resource ); + } + else + { + throw new LockClientStoppedException( this ); + } } } } diff --git a/community/kernel/src/test/java/org/neo4j/kernel/impl/transaction/state/RelationshipCreatorTest.java b/community/kernel/src/test/java/org/neo4j/kernel/impl/transaction/state/RelationshipCreatorTest.java index a3f806b3b9d74..7d1f5af32912a 100644 --- a/community/kernel/src/test/java/org/neo4j/kernel/impl/transaction/state/RelationshipCreatorTest.java +++ b/community/kernel/src/test/java/org/neo4j/kernel/impl/transaction/state/RelationshipCreatorTest.java @@ -115,11 +115,14 @@ public Tracker( NeoStores neoStores ) } @Override - public void acquireExclusive( Locks.ResourceType resourceType, long resourceId ) + public void acquireExclusive( Locks.ResourceType resourceType, long... resourceIds ) throws AcquireLockTimeoutException { assertEquals( ResourceTypes.RELATIONSHIP, resourceType ); - relationshipLocksAcquired.add( resourceId ); + for ( long resourceId : resourceIds ) + { + relationshipLocksAcquired.add( resourceId ); + } } protected void changingRelationship( long relId ) diff --git a/enterprise/ha/src/main/java/org/neo4j/kernel/ha/lock/SlaveLocksClient.java b/enterprise/ha/src/main/java/org/neo4j/kernel/ha/lock/SlaveLocksClient.java index 94350ff4e50e2..df20788987e99 100644 --- a/enterprise/ha/src/main/java/org/neo4j/kernel/ha/lock/SlaveLocksClient.java +++ b/enterprise/ha/src/main/java/org/neo4j/kernel/ha/lock/SlaveLocksClient.java @@ -100,54 +100,60 @@ private Map getLockMap( } @Override - public void acquireShared( Locks.ResourceType resourceType, long resourceId ) throws AcquireLockTimeoutException + public void acquireShared( Locks.ResourceType resourceType, long... resourceIds ) throws AcquireLockTimeoutException { assertNotStopped(); Map lockMap = getLockMap( sharedLocks, resourceType ); - AtomicInteger preExistingLock = lockMap.get( resourceId ); - if ( preExistingLock != null ) + for ( long resourceId : resourceIds ) { - // We already hold this lock, just increment the local reference count - preExistingLock.incrementAndGet(); - } - else if ( getReadLockOnMaster( resourceType, resourceId ) ) - { - if ( client.trySharedLock( resourceType, resourceId ) ) + AtomicInteger preExistingLock = lockMap.get( resourceId ); + if ( preExistingLock != null ) { - lockMap.put( resourceId, new AtomicInteger( 1 ) ); + // We already hold this lock, just increment the local reference count + preExistingLock.incrementAndGet(); } - else + else if ( getReadLockOnMaster( resourceType, resourceId ) ) { - throw new LocalDeadlockDetectedException( client, localLockManager, resourceType, resourceId, READ ); + if ( client.trySharedLock( resourceType, resourceId ) ) + { + lockMap.put( resourceId, new AtomicInteger( 1 ) ); + } + else + { + throw new LocalDeadlockDetectedException( client, localLockManager, resourceType, resourceId, READ ); + } } } } @Override - public void acquireExclusive( Locks.ResourceType resourceType, long resourceId ) throws + public void acquireExclusive( Locks.ResourceType resourceType, long... resourceIds ) throws AcquireLockTimeoutException { assertNotStopped(); Map lockMap = getLockMap( exclusiveLocks, resourceType ); - AtomicInteger preExistingLock = lockMap.get( resourceId ); - if ( preExistingLock != null ) + for ( long resourceId : resourceIds ) { - // We already hold this lock, just increment the local reference count - preExistingLock.incrementAndGet(); - } - else if ( acquireExclusiveOnMaster( resourceType, resourceId ) ) - { - if ( client.tryExclusiveLock( resourceType, resourceId ) ) + AtomicInteger preExistingLock = lockMap.get( resourceId ); + if ( preExistingLock != null ) { - lockMap.put( resourceId, new AtomicInteger( 1 ) ); + // We already hold this lock, just increment the local reference count + preExistingLock.incrementAndGet(); } - else + else if ( acquireExclusiveOnMaster( resourceType, resourceId ) ) { - throw new LocalDeadlockDetectedException( client, localLockManager, resourceType, resourceId, WRITE ); + if ( client.tryExclusiveLock( resourceType, resourceId ) ) + { + lockMap.put( resourceId, new AtomicInteger( 1 ) ); + } + else + { + throw new LocalDeadlockDetectedException( client, localLockManager, resourceType, resourceId, WRITE ); + } } } } diff --git a/enterprise/kernel/src/main/java/org/neo4j/kernel/impl/enterprise/lock/forseti/ForsetiClient.java b/enterprise/kernel/src/main/java/org/neo4j/kernel/impl/enterprise/lock/forseti/ForsetiClient.java index 433fccb105d7d..415efeef926e1 100644 --- a/enterprise/kernel/src/main/java/org/neo4j/kernel/impl/enterprise/lock/forseti/ForsetiClient.java +++ b/enterprise/kernel/src/main/java/org/neo4j/kernel/impl/enterprise/lock/forseti/ForsetiClient.java @@ -118,7 +118,7 @@ public void reset() } @Override - public void acquireShared( Locks.ResourceType resourceType, long resourceId ) throws AcquireLockTimeoutException + public void acquireShared( Locks.ResourceType resourceType, long... resourceIds ) throws AcquireLockTimeoutException { stateHolder.incrementActiveClients( this ); @@ -131,87 +131,85 @@ public void acquireShared( Locks.ResourceType resourceType, long resourceId ) th PrimitiveLongIntMap heldShareLocks = sharedLockCounts[resourceType.typeId()]; PrimitiveLongIntMap heldExclusiveLocks = exclusiveLockCounts[resourceType.typeId()]; - // First, check if we already hold this as a shared lock - int heldCount = heldShareLocks.get( resourceId ); - if(heldCount != -1) - { - // We already have a lock on this, just increment our local reference counter. - heldShareLocks.put( resourceId, heldCount + 1 ); - return; - } - - // Second, check if we hold it as an exclusive lock - if( heldExclusiveLocks.containsKey( resourceId ) ) + for ( long resourceId : resourceIds ) { - // We already have an exclusive lock, so just leave that in place. When the exclusive lock is released, - // it will be automatically downgraded to a shared lock, since we bumped the share lock reference count. - heldShareLocks.put( resourceId, 1 ); - return; - } + // First, check if we already hold this as a shared lock + int heldCount = heldShareLocks.get( resourceId ); + if(heldCount != -1) + { + // We already have a lock on this, just increment our local reference counter. + heldShareLocks.put( resourceId, heldCount + 1 ); + return; + } + // Second, check if we hold it as an exclusive lock + if( heldExclusiveLocks.containsKey( resourceId ) ) + { + // We already have an exclusive lock, so just leave that in place. When the exclusive lock is released, + // it will be automatically downgraded to a shared lock, since we bumped the share lock reference count. + heldShareLocks.put( resourceId, 1 ); + return; + } + // We don't hold the lock, so we need to grab it via the global lock map + int tries = 0; + SharedLock mySharedLock = null; + // Retry loop + while(true) + { + assertNotStopped(); - // We don't hold the lock, so we need to grab it via the global lock map - int tries = 0; - SharedLock mySharedLock = null; + // Check if there is a lock for this entity in the map + ForsetiLockManager.Lock existingLock = lockMap.get( resourceId ); - // Retry loop - while(true) - { - assertNotStopped(); + // No lock + if(existingLock == null) + { + // Try to create a new shared lock + if(mySharedLock == null) + { + mySharedLock = new SharedLock( this ); + } - // Check if there is a lock for this entity in the map - ForsetiLockManager.Lock existingLock = lockMap.get( resourceId ); + if(lockMap.putIfAbsent( resourceId, mySharedLock ) == null) + { + // Success, we now hold the shared lock. + break; + } + else + { + continue; + } + } - // No lock - if(existingLock == null) - { - // Try to create a new shared lock - if(mySharedLock == null) + // Someone holds shared lock on this entity, try and get in on that action + else if(existingLock instanceof SharedLock) { - mySharedLock = new SharedLock( this ); + if(((SharedLock)existingLock).acquire(this)) + { + // Success! + break; + } } - if(lockMap.putIfAbsent( resourceId, mySharedLock ) == null) + // Someone holds an exclusive lock on this entity + else if(existingLock instanceof ExclusiveLock) { - // Success, we now hold the shared lock. - break; + // We need to wait, just let the loop run. } else { - continue; + throw new UnsupportedOperationException( "Unknown lock type: " + existingLock ); } - } - // Someone holds shared lock on this entity, try and get in on that action - else if(existingLock instanceof SharedLock) - { - if(((SharedLock)existingLock).acquire(this)) - { - // Success! - break; - } - } + applyWaitStrategy( resourceType, tries++ ); - // Someone holds an exclusive lock on this entity - else if(existingLock instanceof ExclusiveLock) - { - // We need to wait, just let the loop run. - } - else - { - throw new UnsupportedOperationException( "Unknown lock type: " + existingLock ); + // And take note of who we are waiting for. This is used for deadlock detection. + markAsWaitingFor( existingLock, resourceType, resourceId ); } - - applyWaitStrategy( resourceType, tries++ ); - - // And take note of who we are waiting for. This is used for deadlock detection. - markAsWaitingFor( existingLock, resourceType, resourceId ); + // Got the lock, no longer waiting for anyone. + clearWaitList(); + // Make a local note about the fact that we now hold this lock + heldShareLocks.put( resourceId, 1 ); } - - // Got the lock, no longer waiting for anyone. - clearWaitList(); - - // Make a local note about the fact that we now hold this lock - heldShareLocks.put( resourceId, 1 ); } finally { @@ -220,7 +218,7 @@ else if(existingLock instanceof ExclusiveLock) } @Override - public void acquireExclusive( Locks.ResourceType resourceType, long resourceId ) throws AcquireLockTimeoutException + public void acquireExclusive( Locks.ResourceType resourceType, long... resourceIds ) throws AcquireLockTimeoutException { // For details on how this works, refer to the acquireShared method call, as the two are very similar @@ -231,40 +229,40 @@ public void acquireExclusive( Locks.ResourceType resourceType, long resourceId ) ConcurrentMap lockMap = lockMaps[resourceType.typeId()]; PrimitiveLongIntMap heldLocks = exclusiveLockCounts[resourceType.typeId()]; - int heldCount = heldLocks.get( resourceId ); - if(heldCount != -1) + for ( long resourceId : resourceIds ) { - // We already have a lock on this, just increment our local reference counter. - heldLocks.put( resourceId, heldCount + 1 ); - return; - } - - // Grab the global lock - ForsetiLockManager.Lock existingLock; - int tries = 0; - while( (existingLock = lockMap.putIfAbsent( resourceId, myExclusiveLock )) != null) - { - assertNotStopped(); - - // If this is a shared lock: - // Given a grace period of tries (to try and not starve readers), grab an update lock and wait for it - // to convert to an exclusive lock. - if( tries > 50 && existingLock instanceof SharedLock) + int heldCount = heldLocks.get( resourceId ); + if(heldCount != -1) + { + // We already have a lock on this, just increment our local reference counter. + heldLocks.put( resourceId, heldCount + 1 ); + return; + } + // Grab the global lock + ForsetiLockManager.Lock existingLock; + int tries = 0; + while( (existingLock = lockMap.putIfAbsent( resourceId, myExclusiveLock )) != null) { - // Then we should upgrade that lock - SharedLock sharedLock = (SharedLock) existingLock; - if ( tryUpgradeSharedToExclusive( resourceType, lockMap, resourceId, sharedLock ) ) + assertNotStopped(); + + // If this is a shared lock: + // Given a grace period of tries (to try and not starve readers), grab an update lock and wait for it + // to convert to an exclusive lock. + if( tries > 50 && existingLock instanceof SharedLock) { - break; + // Then we should upgrade that lock + SharedLock sharedLock = (SharedLock) existingLock; + if ( tryUpgradeSharedToExclusive( resourceType, lockMap, resourceId, sharedLock ) ) + { + break; + } } + applyWaitStrategy( resourceType, tries++ ); + markAsWaitingFor( existingLock, resourceType, resourceId ); } - - applyWaitStrategy( resourceType, tries++ ); - markAsWaitingFor( existingLock, resourceType, resourceId ); + clearWaitList(); + heldLocks.put( resourceId, 1 ); } - - clearWaitList(); - heldLocks.put( resourceId, 1 ); } finally {