use of org.janusgraph.diskstorage.TemporaryBackendException in project janusgraph by JanusGraph.
the class ConsistentKeyLocker method writeSingleLock.
/**
* Try to write a lock record remotely up to the configured number of
* times. If the store produces
* {@link TemporaryLockingException}, then we'll call mutate again to add a
* new column with an updated timestamp and to delete the column that tried
* to write when the store threw an exception. We continue like that up to
* the retry limit. If the store throws anything else, such as an unchecked
* exception or a {@link org.janusgraph.diskstorage.PermanentBackendException}, then we'll try to
* delete whatever we added and return without further retries.
*
* @param lockID lock to acquire
* @param txh transaction
* @return the timestamp, in nanoseconds since UNIX Epoch, on the lock
* column that we successfully wrote to the store
* @throws TemporaryLockingException if the lock retry count is exceeded without successfully
* writing the lock in less than the wait limit
* @throws Throwable if the storage layer throws anything else
*/
@Override
protected ConsistentKeyLockStatus writeSingleLock(KeyColumn lockID, StoreTransaction txh) throws Throwable {
final StaticBuffer lockKey = serializer.toLockKey(lockID.getKey(), lockID.getColumn());
StaticBuffer oldLockCol = null;
for (int i = 0; i < lockRetryCount; i++) {
WriteResult wr = tryWriteLockOnce(lockKey, oldLockCol, txh);
if (wr.isSuccessful() && wr.getDuration().compareTo(lockWait) <= 0) {
final Instant writeInstant = wr.getWriteTimestamp();
final Instant expireInstant = writeInstant.plus(lockExpire);
return new ConsistentKeyLockStatus(writeInstant, expireInstant);
}
oldLockCol = wr.getLockCol();
handleMutationFailure(lockID, lockKey, wr, txh);
}
tryDeleteLockOnce(lockKey, oldLockCol, txh);
// TODO log exception or successful too-slow write here
throw new TemporaryBackendException("Lock write retry count exceeded");
}
use of org.janusgraph.diskstorage.TemporaryBackendException in project janusgraph by JanusGraph.
the class ConsistentKeyLocker method deleteSingleLock.
@Override
protected void deleteSingleLock(KeyColumn kc, ConsistentKeyLockStatus ls, StoreTransaction tx) {
List<StaticBuffer> deletions = Collections.singletonList(serializer.toLockCol(ls.getWriteTimestamp(), rid, times));
for (int i = 0; i < lockRetryCount; i++) {
StoreTransaction newTx = null;
try {
newTx = overrideTimestamp(tx, times.getTime());
store.mutate(serializer.toLockKey(kc.getKey(), kc.getColumn()), Collections.emptyList(), deletions, newTx);
newTx.commit();
newTx = null;
return;
} catch (TemporaryBackendException e) {
log.warn("Temporary storage exception while deleting lock", e);
// don't return -- iterate and retry
} catch (BackendException e) {
log.error("Storage exception while deleting lock", e);
// give up on this lock
return;
} finally {
rollbackIfNotNull(newTx);
}
}
}
use of org.janusgraph.diskstorage.TemporaryBackendException in project janusgraph by JanusGraph.
the class ConsistentKeyLockerTest method testWriteLockThrowsExceptionAfterMaxStoreTimeouts.
/**
* Test locker when all three attempts to write a lock succeed but take
* longer than the wait limit. We expect the locker to delete all three
* columns that it wrote and locally unlock the KeyColumn, then emit an
* exception.
*
* @throws org.janusgraph.diskstorage.BackendException shouldn't happen
*/
@Test
public void testWriteLockThrowsExceptionAfterMaxStoreTimeouts() throws BackendException {
expect(lockState.has(defaultTx, defaultLockID)).andReturn(false);
recordSuccessfulLocalLock();
StaticBuffer firstCol = recordSuccessfulLockWrite(5, ChronoUnit.SECONDS, null).col;
StaticBuffer secondCol = recordSuccessfulLockWrite(5, ChronoUnit.SECONDS, firstCol).col;
StaticBuffer thirdCol = recordSuccessfulLockWrite(5, ChronoUnit.SECONDS, secondCol).col;
recordSuccessfulLockDelete(thirdCol);
recordSuccessfulLocalUnlock();
ctrl.replay();
BackendException expected = null;
try {
// SUT
locker.writeLock(defaultLockID, defaultTx);
} catch (TemporaryBackendException e) {
expected = e;
}
assertNotNull(expected);
}
use of org.janusgraph.diskstorage.TemporaryBackendException in project janusgraph by JanusGraph.
the class ConsistentKeyLockerTest method testDeleteLocksSkipsToNextLockAfterMaxTemporaryStorageExceptions.
/**
* If lock deletion exceeds the temporary exception retry count when trying
* to delete a lock, it should move onto the next lock rather than returning
* and potentially leaving the remaining locks alone (not deleted).
*
* @throws org.janusgraph.diskstorage.BackendException shouldn't happen
*/
@Test
public void testDeleteLocksSkipsToNextLockAfterMaxTemporaryStorageExceptions() throws BackendException {
ConsistentKeyLockStatus defaultLS = makeStatusNow();
currentTimeNS = currentTimeNS.plusNanos(1);
expect(lockState.getLocksForTx(defaultTx)).andReturn(Maps.newLinkedHashMap(ImmutableMap.of(defaultLockID, defaultLS)));
expectDeleteLock(defaultLockID, defaultLockKey, defaultLS, new TemporaryBackendException("Storage cluster is busy"), new TemporaryBackendException("Storage cluster is busier"), new TemporaryBackendException("Storage cluster has reached peak business"));
ctrl.replay();
locker.deleteLocks(defaultTx);
}
use of org.janusgraph.diskstorage.TemporaryBackendException in project janusgraph by JanusGraph.
the class ConsistentKeyLockerTest method testCheckLocksRetriesAfterSingleTemporaryStorageException.
/**
* The checker should retry getSlice() in the face of a
* TemporaryStorageException so long as the number of exceptional
* getSlice()s is fewer than the lock retry count. The retry count applies
* on a per-lock basis.
*
* @throws org.janusgraph.diskstorage.BackendException shouldn't happen
* @throws InterruptedException shouldn't happen
*/
@Test
public void testCheckLocksRetriesAfterSingleTemporaryStorageException() throws BackendException, InterruptedException {
// Setup one lock column
StaticBuffer lockCol = codec.toLockCol(currentTimeNS, defaultLockRid, times);
ConsistentKeyLockStatus lockStatus = makeStatusNow();
currentTimeNS = currentTimeNS.plusNanos(1);
expect(lockState.getLocksForTx(defaultTx)).andReturn(ImmutableMap.of(defaultLockID, lockStatus));
expectSleepAfterWritingLock(lockStatus);
// First getSlice will fail
TemporaryBackendException tse = new TemporaryBackendException("Storage cluster will be right back");
recordExceptionalLockGetSlice(tse);
// Second getSlice will succeed
recordLockGetSliceAndReturnSingleEntry(StaticArrayEntry.of(lockCol, defaultLockVal));
ctrl.replay();
locker.checkLocks(defaultTx);
// TODO run again with two locks instead of one and show that the retry count applies on a per-lock basis
}
Aggregations