use of org.janusgraph.diskstorage.locking.PermanentLockingException in project janusgraph by JanusGraph.
the class JanusGraphTest method executeLockConflictingTransactionJobs.
/**
* Executes a transaction job in two parallel transactions under the assumptions that the two transactions
* should conflict and the one committed later should throw a locking exception due to mismatch of expected value.
*
* @param graph
* @param job
*/
private void executeLockConflictingTransactionJobs(JanusGraph graph, TransactionJob job) {
JanusGraphTransaction tx1 = graph.newTransaction();
JanusGraphTransaction tx2 = graph.newTransaction();
job.run(tx1);
job.run(tx2);
/*
* Under pessimistic locking, tx1 should abort and tx2 should commit.
* Under optimistic locking, tx1 may commit and tx2 may abort.
*/
JanusGraphException janusGraphException;
if (isLockingOptimistic()) {
tx1.commit();
janusGraphException = assertThrows(JanusGraphException.class, tx2::commit);
} else {
janusGraphException = assertThrows(JanusGraphException.class, tx1::commit);
tx2.commit();
}
Throwable rootCause = janusGraphException.getCause().getCause();
assertTrue(rootCause instanceof PermanentLockingException);
assertTrue(rootCause.getMessage().contains("Expected value mismatch for"));
}
use of org.janusgraph.diskstorage.locking.PermanentLockingException in project janusgraph by JanusGraph.
the class ExpectedValueCheckingStore method acquireLock.
/**
* {@inheritDoc}
* <p>
* This implementation supports locking when {@code lockStore} is non-null.
* <p>
* Consider the following scenario. This method is called twice with
* identical key, column, and txh arguments, but with different
* expectedValue arguments in each call. In testing, it seems JanusGraph's
* graphdb requires that implementations discard the second expectedValue
* and, when checking expectedValues vs actual values just prior to mutate,
* only the initial expectedValue argument should be considered.
*/
@Override
public void acquireLock(StaticBuffer key, StaticBuffer column, StaticBuffer expectedValue, StoreTransaction txh) throws BackendException {
if (locker != null) {
ExpectedValueCheckingTransaction tx = (ExpectedValueCheckingTransaction) txh;
if (tx.isMutationStarted())
throw new PermanentLockingException("Attempted to obtain a lock after mutations had been persisted");
KeyColumn lockID = new KeyColumn(key, column);
log.debug("Attempting to acquireLock on {} ev={}", lockID, expectedValue);
locker.writeLock(lockID, tx.getConsistentTx());
tx.storeExpectedValue(this, lockID, expectedValue);
} else {
store.acquireLock(key, column, expectedValue, unwrapTx(txh));
}
}
use of org.janusgraph.diskstorage.locking.PermanentLockingException in project janusgraph by JanusGraph.
the class ExpectedValueCheckingTransaction method checkSingleExpectedValueUnsafe.
private void checkSingleExpectedValueUnsafe(final KeyColumn kc, final StaticBuffer ev, final ExpectedValueCheckingStore store) throws BackendException {
final StaticBuffer nextBuf = BufferUtil.nextBiggerBuffer(kc.getColumn());
KeySliceQuery ksq = new KeySliceQuery(kc.getKey(), kc.getColumn(), nextBuf);
// Call getSlice on the wrapped store using the quorum+ consistency tx
Iterable<Entry> actualEntries = store.getBackingStore().getSlice(ksq, strongConsistentTx);
if (null == actualEntries)
actualEntries = Collections.emptyList();
/*
* Discard any columns which do not exactly match kc.getColumn().
*
* For example, it's possible that the slice returned columns which for
* which kc.getColumn() is a prefix.
*/
actualEntries = Iterables.filter(actualEntries, input -> {
if (!input.getColumn().equals(kc.getColumn())) {
log.debug("Dropping entry {} (only accepting column {})", input, kc.getColumn());
return false;
}
log.debug("Accepting entry {}", input);
return true;
});
// Extract values from remaining Entry instances
final Iterable<StaticBuffer> actualValues = Iterables.transform(actualEntries, e -> {
final StaticBuffer actualCol = e.getColumnAs(StaticBuffer.STATIC_FACTORY);
assert null != actualCol;
assert null != kc.getColumn();
assert 0 >= kc.getColumn().compareTo(actualCol);
assert 0 > actualCol.compareTo(nextBuf);
return e.getValueAs(StaticBuffer.STATIC_FACTORY);
});
final Iterable<StaticBuffer> expectedValues;
if (null == ev) {
expectedValues = Collections.emptyList();
} else {
expectedValues = Collections.singletonList(ev);
}
if (!Iterables.elementsEqual(expectedValues, actualValues)) {
throw new PermanentLockingException("Expected value mismatch for " + kc + ": expected=" + expectedValues + " vs actual=" + actualValues + " (store=" + store.getName() + ")");
}
}
use of org.janusgraph.diskstorage.locking.PermanentLockingException in project janusgraph by JanusGraph.
the class JanusGraphTest method executeParallelTransactions.
/**
* Execute multiple transactions in different threads concurrently to test locking
* @param job A transaction job which triggers locking
* @param concurrency Number of threads, each of which runs a transaction
* @return [number of successful transactions, number of transactions failed due to local lock contention]
*/
private int[] executeParallelTransactions(final TransactionJob job, int concurrency) {
final CountDownLatch startLatch = new CountDownLatch(concurrency);
final CountDownLatch finishLatch = new CountDownLatch(concurrency);
final AtomicInteger txSuccess = new AtomicInteger(0);
final AtomicInteger lockingExCount = new AtomicInteger(0);
for (int i = 0; i < concurrency; i++) {
new Thread() {
@Override
public void run() {
JanusGraphTransaction tx = graph.newTransaction();
try {
job.run(tx);
// we force all threads to wait until they are all ready to commit, so that we can test lock
// contention
awaitAllThreadsReady();
tx.commit();
txSuccess.incrementAndGet();
} catch (Exception ex) {
ex.printStackTrace();
if (tx.isOpen())
tx.rollback();
if (ex.getCause() instanceof PermanentLockingException && ex.getCause().getMessage().contains("Local lock contention")) {
lockingExCount.incrementAndGet();
}
} finally {
finishLatch.countDown();
}
}
private void awaitAllThreadsReady() {
startLatch.countDown();
try {
startLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
}
try {
finishLatch.await(10000, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
return new int[] { txSuccess.get(), lockingExCount.get() };
}
Aggregations