@Override public void acquireLock(StaticBuffer key, StaticBuffer column, StaticBuffer expectedValue, StoreTransaction txh) throws BackendException { store.acquireLock(key,column,expectedValue,txh); }
@Override public void acquireLock(StaticBuffer key, StaticBuffer column, StaticBuffer expectedValue, StoreTransaction txh) throws BackendException { store.acquireLock(key,column,expectedValue,unwrapTx(txh)); }
@Override public void acquireLock(final StaticBuffer key, final StaticBuffer column, final StaticBuffer expectedValue, final StoreTransaction txh) throws BackendException { runWithMetrics(txh, metricsStoreName, M_ACQUIRE_LOCK, (StorageCallable<Void>) () -> { backend.acquireLock(key, column, expectedValue, txh); return null; }); }
store[0].acquireLock(k, k, null, tx[0][0]); break; store[0].acquireLock(k, k, null, tx[0][0]); Thread.sleep(loopDurationMS / steps); store[0].acquireLock(k, k, null, tx[0][1]);
private void tryLocks(KeyColumnValueStore s1, StoreTransaction tx1, KeyColumnValueStore s2, StoreTransaction tx2, boolean detectLocally) throws BackendException, InterruptedException { s1.acquireLock(k, k, null, tx1); // Require local lock contention, if requested by our caller // Remote lock contention is checked by separate cases if (detectLocally) { try { s2.acquireLock(k, k, null, tx2); Assert.fail("Expected lock contention between transactions did not occur"); } catch (BackendException e) { Assert.assertTrue(e instanceof PermanentLockingException || e instanceof TemporaryLockingException); } } // Let the original lock expire Thread.sleep(EXPIRE_MS + 100L); // This should succeed now that the original lock is expired s2.acquireLock(k, k, null, tx2); // Mutate to check for remote contention s2.mutate(k, Collections.singletonList(StaticArrayEntry.of(c2, v2)), NO_DELETIONS, tx2); }
@Test public void transactionMayReenterLock() throws BackendException { store[0].acquireLock(k, c1, null, tx[0][0]); store[0].acquireLock(k, c1, null, tx[0][0]); store[0].acquireLock(k, c1, null, tx[0][0]); store[0].mutate(k, Collections.singletonList(StaticArrayEntry.of(c1, v1)), NO_DELETIONS, tx[0][0]); tx[0][0].commit(); tx[0][0] = newTransaction(manager[0]); Assert.assertEquals(v1, KCVSUtil.get(store[0], k, c1, tx[0][0])); }
@Test public void testLocalLockContention() throws BackendException { store[0].acquireLock(k, c1, null, tx[0][0]); try { store[0].acquireLock(k, c1, null, tx[0][1]); Assert.fail("Lock contention exception not thrown"); } catch (BackendException e) { Assert.assertTrue(e instanceof PermanentLockingException || e instanceof TemporaryLockingException); } try { store[0].acquireLock(k, c1, null, tx[0][1]); Assert.fail("Lock contention exception not thrown (2nd try)"); } catch (BackendException e) { Assert.assertTrue(e instanceof PermanentLockingException || e instanceof TemporaryLockingException); } }
@Test(expected = PermanentLockingException.class) public void expectedValueMismatchCausesMutateFailure() throws BackendException { store[0].acquireLock(k, c1, v1, tx[0][0]); store[0].mutate(k, Collections.singletonList(StaticArrayEntry.of(c1, v1)), NO_DELETIONS, tx[0][0]); }
@Override public void run() { // Catch & log exceptions for (int opIndex = 0; opIndex < opCount; opIndex++) { StoreTransaction tx = null; try { tx = newTransaction(manager); store.acquireLock(toLock, toLock, null, tx); store.mutate(toLock, ImmutableList.of(), Collections.singletonList(toLock), tx); tx.commit(); succeeded++; } catch (TemporaryLockingException e) { temporaryFailures++; } catch (Throwable t) { log.error("Unexpected locking-related exception on iteration " + (opIndex + 1) + "/" + opCount, t); } } /* * This latch is the only thing guaranteeing that succeeded's true * value is observable by other threads once we're done with run() * and the latch's await() method returns. */ doneLatch.countDown(); } }
@Test public void singleLockAndUnlock() throws BackendException { store[0].acquireLock(k, c1, null, tx[0][0]); store[0].mutate(k, Collections.singletonList(StaticArrayEntry.of(c1, v1)), NO_DELETIONS, tx[0][0]); tx[0][0].commit(); tx[0][0] = newTransaction(manager[0]); Assert.assertEquals(v1, KCVSUtil.get(store[0], k, c1, tx[0][0])); }
@Test public void testRemoteLockContention() throws InterruptedException, BackendException { store[0].acquireLock(k, c1, null, tx[0][0]); store[1].acquireLock(k, c1, null, tx[1][0]); } catch (BackendException e) { /* Lock attempts between hosts with different LocalLockMediators,
private void tryWrites(KeyColumnValueStore store1, KeyColumnValueStoreManager keyColumnValueStoreManager, StoreTransaction tx1, KeyColumnValueStore store2, StoreTransaction tx2) throws BackendException { Assert.assertNull(KCVSUtil.get(store1, k, c1, tx1)); Assert.assertNull(KCVSUtil.get(store2, k, c2, tx2)); store1.acquireLock(k, c1, null, tx1); store2.acquireLock(k, c2, null, tx2); store1.mutate(k, Collections.singletonList(StaticArrayEntry.of(c1, v1)), NO_DELETIONS, tx1); store2.mutate(k, Collections.singletonList(StaticArrayEntry.of(c2, v2)), NO_DELETIONS, tx2); tx1.commit(); if (tx2 != tx1) tx2.commit(); StoreTransaction transaction = newTransaction(keyColumnValueStoreManager); Assert.assertEquals(v1, KCVSUtil.get(store1, k, c1, transaction)); Assert.assertEquals(v2, KCVSUtil.get(store2, k, c2, transaction)); transaction.commit(); }
/** * {@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)); } }
@Test public void testMutateManyWithLockUsesConsistentTx() throws BackendException { final ImmutableList<Entry> adds = ImmutableList.of(StaticArrayEntry.of(DATA_COL, DATA_VAL)); final ImmutableList<StaticBuffer> deletions = ImmutableList.of(); Map<String, Map<StaticBuffer, KCVMutation>> mutations = ImmutableMap.of(STORE_NAME, ImmutableMap.of(DATA_KEY, new KCVMutation(adds, deletions))); final KeyColumn kc = new KeyColumn(LOCK_KEY, LOCK_COL); // Acquire a lock backingLocker.writeLock(kc, consistentTx); // 2. Run mutateMany // 2.1. Check locks & expected values before mutating data backingLocker.checkLocks(consistentTx); StaticBuffer nextBuf = BufferUtil.nextBiggerBuffer(kc.getColumn()); KeySliceQuery expectedValueQuery = new KeySliceQuery(kc.getKey(), kc.getColumn(), nextBuf); expect(backingStore.getSlice(expectedValueQuery, consistentTx)) // expected value read must use strong consistency .andReturn(StaticArrayEntryList.of(StaticArrayEntry.of(LOCK_COL, LOCK_VAL))); // 2.2. Run mutateMany on backing manager to modify data backingManager.mutateMany(mutations, consistentTx); // writes by txs with locks must use strong consistency ctrl.replay(); // Lock acquisition expectStore.acquireLock(LOCK_KEY, LOCK_COL, LOCK_VAL, expectTx); // Mutate expectManager.mutateMany(mutations, expectTx); }
@Test public void testMutateWithLockUsesConsistentTx() throws BackendException { final ImmutableList<Entry> adds = ImmutableList.of(StaticArrayEntry.of(DATA_COL, DATA_VAL)); final ImmutableList<StaticBuffer> deletions = ImmutableList.of(); final KeyColumn kc = new KeyColumn(LOCK_KEY, LOCK_COL); // 1. Acquire a lock backingLocker.writeLock(kc, consistentTx); // 2. Run a mutation // N.B. mutation coordinates do not overlap with the lock, but consistentTx should be used anyway // 2.1. Check locks & expected values before mutating data backingLocker.checkLocks(consistentTx); StaticBuffer nextBuf = BufferUtil.nextBiggerBuffer(kc.getColumn()); KeySliceQuery expectedValueQuery = new KeySliceQuery(kc.getKey(), kc.getColumn(), nextBuf); expect(backingStore.getSlice(expectedValueQuery, consistentTx)) // expected value read must use strong consistency .andReturn(StaticArrayEntryList.of(StaticArrayEntry.of(LOCK_COL, LOCK_VAL))); // 2.2. Mutate data backingStore.mutate(DATA_KEY, adds, deletions, consistentTx); // writes by txs with locks must use strong consistency ctrl.replay(); // 1. Lock acquisition expectStore.acquireLock(LOCK_KEY, LOCK_COL, LOCK_VAL, expectTx); // 2. Mutate expectStore.mutate(DATA_KEY, adds, deletions, expectTx); }
s.acquireLock(key, col, null, tx);