use of org.neo4j.lock.LockWaitEvent in project neo4j by neo4j.
the class ForsetiClient method tryUpgradeToExclusiveWithShareLockHeld.
/**
* Attempt to upgrade a share lock that we hold to an exclusive lock.
*/
private boolean tryUpgradeToExclusiveWithShareLockHeld(LockTracer tracer, LockWaitEvent priorEvent, ResourceType resourceType, long resourceId, SharedLock sharedLock, int tries, long waitStartNano) throws AcquireLockTimeoutException {
if (sharedLock.tryAcquireUpdateLock(this)) {
LockWaitEvent waitEvent = null;
try {
// Now we just wait for all clients to release the the share lock
while (sharedLock.numberOfHolders() > 1) {
assertValid(waitStartNano, resourceType, resourceId);
if (waitEvent == null && priorEvent == null) {
waitEvent = tracer.waitForLock(EXCLUSIVE, resourceType, userTransactionId, resourceId);
}
waitFor(sharedLock, resourceType, resourceId, tries++);
}
return true;
} catch (Throwable e) {
sharedLock.releaseUpdateLock();
if (e instanceof DeadlockDetectedException || e instanceof LockClientStoppedException) {
throw (RuntimeException) e;
}
throw new TransactionFailureException("Failed to upgrade shared lock to exclusive: " + sharedLock, e);
} finally {
if (waitEvent != null) {
waitEvent.close();
}
clearWaitList();
waitingForLock = null;
}
}
return false;
}
use of org.neo4j.lock.LockWaitEvent in project neo4j by neo4j.
the class ForsetiClient method acquireExclusive.
@Override
public void acquireExclusive(LockTracer tracer, ResourceType resourceType, long... resourceIds) throws AcquireLockTimeoutException {
hasLocks = true;
stateHolder.incrementActiveClients(this);
LockWaitEvent waitEvent = null;
try {
ConcurrentMap<Long, ForsetiLockManager.Lock> lockMap = lockMaps[resourceType.typeId()];
HeapTrackingLongIntHashMap heldLocks = getExclusiveLockCount(resourceType);
for (long resourceId : resourceIds) {
int heldCount = heldLocks.getIfAbsent(resourceId, NO_CLIENT_ID);
if (heldCount != NO_CLIENT_ID) {
// We already have a lock on this, just increment our local reference counter.
heldLocks.put(resourceId, Math.incrementExact(heldCount));
continue;
}
// Grab the global lock
ForsetiLockManager.Lock existingLock;
int tries = 0;
long waitStartNano = clock.nanos();
boolean upgraded = false;
while ((existingLock = lockMap.putIfAbsent(resourceId, myExclusiveLock)) != null) {
assertValid(waitStartNano, resourceType, resourceId);
// for it to convert to an exclusive lock.
if (tries > 50 && existingLock instanceof SharedLock) {
// Then we should upgrade that lock
SharedLock sharedLock = (SharedLock) existingLock;
if (tryUpgradeSharedToExclusive(tracer, waitEvent, resourceType, lockMap, resourceId, sharedLock, waitStartNano)) {
upgraded = true;
break;
}
}
if (waitEvent == null) {
waitEvent = tracer.waitForLock(EXCLUSIVE, resourceType, userTransactionId, resourceId);
}
waitFor(existingLock, resourceType, resourceId, tries++);
}
heldLocks.put(resourceId, 1);
if (!upgraded) {
memoryTracker.allocateHeap(CONCURRENT_NODE_SIZE);
}
}
} finally {
if (waitEvent != null) {
waitEvent.close();
}
clearWaitList();
waitingForLock = null;
stateHolder.decrementActiveClients();
}
}
use of org.neo4j.lock.LockWaitEvent in project neo4j by neo4j.
the class ForsetiClient method acquireShared.
@Override
public void acquireShared(LockTracer tracer, ResourceType resourceType, long... resourceIds) throws AcquireLockTimeoutException {
hasLocks = true;
stateHolder.incrementActiveClients(this);
LockWaitEvent waitEvent = null;
try {
// Grab the global lock map we will be using
ConcurrentMap<Long, ForsetiLockManager.Lock> lockMap = lockMaps[resourceType.typeId()];
// And grab our local lock maps
HeapTrackingLongIntHashMap heldShareLocks = getSharedLockCount(resourceType);
HeapTrackingLongIntHashMap heldExclusiveLocks = getExclusiveLockCount(resourceType);
for (long resourceId : resourceIds) {
// First, check if we already hold this as a shared lock
int heldCount = heldShareLocks.getIfAbsent(resourceId, NO_CLIENT_ID);
if (heldCount != NO_CLIENT_ID) {
// We already have a lock on this, just increment our local reference counter.
heldShareLocks.put(resourceId, Math.incrementExact(heldCount));
continue;
}
// 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);
continue;
}
// We don't hold the lock, so we need to grab it via the global lock map
int tries = 0;
SharedLock mySharedLock = null;
long waitStartNano = clock.nanos();
// Retry loop
while (true) {
assertValid(waitStartNano, resourceType, resourceId);
// Check if there is a lock for this entity in the map
ForsetiLockManager.Lock existingLock = lockMap.get(resourceId);
// No lock
if (existingLock == null) {
// Try to create a new shared lock
if (mySharedLock == null) {
mySharedLock = new SharedLock(this);
}
if (lockMap.putIfAbsent(resourceId, mySharedLock) == null) {
// Success, we now hold the shared lock.
break;
} else {
continue;
}
} else // Someone holds shared lock on this entity, try and get in on that action
if (existingLock instanceof SharedLock) {
if (((SharedLock) existingLock).acquire(this)) {
// Success!
break;
}
} else // Someone holds an exclusive lock on this entity
if (existingLock instanceof ExclusiveLock) {
// We need to wait, just let the loop run.
} else {
throw new UnsupportedOperationException("Unknown lock type: " + existingLock);
}
if (waitEvent == null) {
waitEvent = tracer.waitForLock(SHARED, resourceType, userTransactionId, resourceId);
}
// And take note of who we are waiting for. This is used for deadlock detection.
waitFor(existingLock, resourceType, resourceId, tries++);
}
// Make a local note about the fact that we now hold this lock
heldShareLocks.put(resourceId, 1);
memoryTracker.allocateHeap(CONCURRENT_NODE_SIZE);
}
} finally {
if (waitEvent != null) {
waitEvent.close();
}
clearWaitList();
waitingForLock = null;
stateHolder.decrementActiveClients();
}
}
use of org.neo4j.lock.LockWaitEvent in project neo4j by neo4j.
the class ExecutingQueryTest method shouldTransitionBetweenStates.
@Test
void shouldTransitionBetweenStates() {
// initial
assertEquals("parsing", query.snapshot().status());
// when
query.onObfuscatorReady(null);
// then
assertEquals("planning", query.snapshot().status());
// when
query.onCompilationCompleted(new CompilerInfo("the-planner", "the-runtime", emptyList()), READ_ONLY, null);
// then
assertEquals("planned", query.snapshot().status());
// when
query.onExecutionStarted(new FakeMemoryTracker());
// then
assertEquals("running", query.snapshot().status());
// when
try (LockWaitEvent ignored = lock("NODE", 17)) {
// then
assertEquals("waiting", query.snapshot().status());
}
// then
assertEquals("running", query.snapshot().status());
}
use of org.neo4j.lock.LockWaitEvent in project neo4j by neo4j.
the class ExecutingQueryTest method shouldReportWaitTime.
@Test
void shouldReportWaitTime() {
// given
query.onObfuscatorReady(null);
query.onCompilationCompleted(new CompilerInfo("the-planner", "the-runtime", emptyList()), READ_ONLY, null);
query.onExecutionStarted(new FakeMemoryTracker());
// then
assertEquals("running", query.snapshot().status());
// when
clock.forward(10, TimeUnit.SECONDS);
try (LockWaitEvent ignored = lock("NODE", 17)) {
clock.forward(5, TimeUnit.SECONDS);
// then
QuerySnapshot snapshot = query.snapshot();
assertEquals("waiting", snapshot.status());
assertThat(snapshot.resourceInformation()).containsEntry("waitTimeMillis", 5_000L).containsEntry("resourceType", "NODE").containsEntry("transactionId", 10L).containsEntry("resourceIds", new long[] { 17 });
assertEquals(5_000_000, snapshot.waitTimeMicros());
}
{
QuerySnapshot snapshot = query.snapshot();
assertEquals("running", snapshot.status());
assertEquals(5_000_000, snapshot.waitTimeMicros());
}
// when
clock.forward(2, TimeUnit.SECONDS);
try (LockWaitEvent ignored = lock("RELATIONSHIP", 612)) {
clock.forward(1, TimeUnit.SECONDS);
// then
QuerySnapshot snapshot = query.snapshot();
assertEquals("waiting", snapshot.status());
assertThat(snapshot.resourceInformation()).containsEntry("waitTimeMillis", 1_000L).containsEntry("resourceType", "RELATIONSHIP").containsEntry("transactionId", 10L).containsEntry("resourceIds", new long[] { 612 });
assertEquals(6_000_000, snapshot.waitTimeMicros());
}
{
QuerySnapshot snapshot = query.snapshot();
assertEquals("running", snapshot.status());
assertEquals(6_000_000, snapshot.waitTimeMicros());
}
}
Aggregations