private void runAssertion(CacheOperation operation) throws NotSupportedException, SystemException, HeuristicMixedException, HeuristicRollbackException, InvalidTransactionException, RollbackException { txStatus.reset(); tm.begin(); cache(1).put("k1", "v1"); Transaction k1LockOwner = tm.suspend(); assert lm1.isLocked("k1"); assertEquals(1, txTable1.getLocalTxCount()); tm.begin(); cache(0).put("k2", "v2"); assert lm0.isLocked("k2"); assert !lm1.isLocked("k2"); operation.execute(); assertEquals(1, txTable1.getLocalTxCount()); assertEquals(1, txTable0.getLocalTxCount()); try { tm.commit(); assert false; } catch (RollbackException re) { // expected } assert txStatus.teReceived; assert txStatus.isTxInTableAfterTeOnPrepare; // expect 1 as k1 is locked by the other tx assertEquals( txStatus.numLocksAfterTeOnPrepare, 1, "This would make sure that locks are being released quickly, not waiting for a remote rollback to happen"); assertEquals(0, txTable0.getLocalTxCount()); assertEquals(1, txTable1.getLocalTxCount()); log.trace("Right before second commit"); tm.resume(k1LockOwner); tm.commit(); assertEquals("v1", cache(0).get("k1")); assertEquals("v1", cache(1).get("k1")); assertEquals(0, txTable1.getLocalTxCount()); assertEquals(0, txTable1.getLocalTxCount()); assertEquals(0, lm0.getNumberOfLocksHeld()); assertEquals(0, lm1.getNumberOfLocksHeld()); }
@Override public final BasicInvocationStage visitInvalidateCommand( InvocationContext ctx, InvalidateCommand command) throws Throwable { if (hasSkipLocking(command)) { return invokeNext(ctx, command); } try { lockAllAndRecord(ctx, Arrays.asList(command.getKeys()), getLockTimeoutMillis(command)); } catch (Throwable t) { lockManager.unlockAll(ctx); } return invokeNext(ctx, command).handle(unlockAllReturnHandler); }
// We need this method in here because of putForExternalRead protected final BasicInvocationStage visitNonTxDataWriteCommand( InvocationContext ctx, DataWriteCommand command) throws Throwable { if (hasSkipLocking(command) || !shouldLockKey(command.getKey())) { return invokeNext(ctx, command); } try { lockAndRecord(ctx, command.getKey(), getLockTimeoutMillis(command)); } catch (Throwable t) { lockManager.unlockAll(ctx); throw t; } return invokeNext(ctx, command).handle(unlockAllReturnHandler); }
public void testSyncReplMap() throws Exception { Integer age; LockManager lm1 = TestingUtil.extractComponent(cache1, LockManager.class); assert lm1.getOwner("age") == null : "lock info is " + lm1.printLockInfo(); LocalListener lis = new LocalListener(); cache1.addListener(lis); lis.put("age", 38); assert lm1.getOwner("age") == null : "lock info is " + lm1.printLockInfo(); cache1.put("name", "Ben"); // value on cache2 must be 38 age = (Integer) cache2.get("age"); assertNotNull("\"age\" obtained from cache2 must be non-null ", age); assertTrue("\"age\" must be 38", age == 38); assert lm1.getOwner("age") == null : "lock info is " + lm1.printLockInfo(); }
@Override public final BasicInvocationStage visitInvalidateL1Command( InvocationContext ctx, InvalidateL1Command command) throws Throwable { if (command.isCausedByALocalWrite(cdl.getAddress())) { if (trace) getLog().trace("Skipping invalidation as the write operation originated here."); return returnWith(null); } if (hasSkipLocking(command)) { return invokeNext(ctx, command); } final Object[] keys = command.getKeys(); if (keys == null || keys.length < 1) { return returnWith(null); } ArrayList<Object> keysToInvalidate = new ArrayList<>(keys.length); for (Object key : keys) { try { lockAndRecord(ctx, key, 0); keysToInvalidate.add(key); } catch (TimeoutException te) { getLog().unableToLockToInvalidate(key, cdl.getAddress()); } } if (keysToInvalidate.isEmpty()) { return returnWith(null); } command.setKeys(keysToInvalidate.toArray()); return invokeNext(ctx, command) .handle( (rCtx, rCommand, rv, t) -> { ((InvalidateL1Command) rCommand).setKeys(keys); if (!rCtx.isInTxScope()) lockManager.unlockAll(rCtx); }); }
@Override public Map<String, Number> call() throws Exception { Map<String, Number> map = new HashMap<>(); Stats stats = remoteCache.getStats(); map.put(AVERAGE_READ_TIME, stats.getAverageReadTime()); map.put(AVERAGE_WRITE_TIME, stats.getAverageWriteTime()); map.put(AVERAGE_REMOVE_TIME, stats.getAverageRemoveTime()); map.put(EVICTIONS, stats.getEvictions()); map.put(HITS, stats.getHits()); map.put(MISSES, stats.getMisses()); final CacheMode cacheMode = getCacheMode(remoteCache); // for replicated caches, we don't need to send the number of entries since it is the same in // all the nodes. if (cacheMode.isDistributed()) { map.put(NUMBER_OF_ENTRIES, stats.getCurrentNumberOfEntries() / numOwners()); } else if (!cacheMode.isReplicated()) { map.put(NUMBER_OF_ENTRIES, stats.getCurrentNumberOfEntries()); } map.put(STORES, stats.getStores()); map.put(REMOVE_HITS, stats.getRemoveHits()); map.put(REMOVE_MISSES, stats.getRemoveMisses()); map.put(TIME_SINCE_START, stats.getTimeSinceStart()); LockManager lockManager = remoteCache.getLockManager(); map.put(NUMBER_OF_LOCKS_HELD, lockManager.getNumberOfLocksHeld()); // number of locks available is not exposed through the LockManager interface map.put(NUMBER_OF_LOCKS_AVAILABLE, 0); // invalidations InvalidationInterceptor invalidationInterceptor = getFirstInterceptorWhichExtends(remoteCache, InvalidationInterceptor.class); if (invalidationInterceptor != null) { map.put(INVALIDATIONS, invalidationInterceptor.getInvalidations()); } else { map.put(INVALIDATIONS, 0); } // passivations PassivationManager pManager = remoteCache.getComponentRegistry().getComponent(PassivationManager.class); if (pManager != null) { map.put(PASSIVATIONS, pManager.getPassivations()); } else { map.put(PASSIVATIONS, 0); } // activations ActivationManager aManager = remoteCache.getComponentRegistry().getComponent(ActivationManager.class); if (pManager != null) { map.put(ACTIVATIONS, aManager.getActivationCount()); } else { map.put(ACTIVATIONS, 0); } // cache loaders ActivationInterceptor aInterceptor = getFirstInterceptorWhichExtends(remoteCache, ActivationInterceptor.class); if (aInterceptor != null) { map.put(CACHE_LOADER_LOADS, aInterceptor.getCacheLoaderLoads()); map.put(CACHE_LOADER_MISSES, aInterceptor.getCacheLoaderMisses()); } else { map.put(CACHE_LOADER_LOADS, 0); map.put(CACHE_LOADER_MISSES, 0); } // cache store CacheWriterInterceptor interceptor = getFirstInterceptorWhichExtends(remoteCache, CacheWriterInterceptor.class); if (interceptor != null) { map.put(CACHE_WRITER_STORES, interceptor.getWritesToTheStores()); } else { map.put(CACHE_WRITER_STORES, 0); } return map; }
/** * Verifies the cache doesn't contain any lock * * @param cache */ public static void assertNoLocks(Cache<?, ?> cache) { LockManager lm = TestingUtil.extractLockManager(cache); for (Object key : cache.keySet()) assert !lm.isLocked(key); }
public void testCommitDoesntWriteAfterRollback() throws Exception { // Start a tx on A: put(k, v1), owners(k) = [B (primary) and C (backup)] // Block the commit on C so that it times out // Wait for the rollback command to be executed on B and C, and for the tx to end // Check that locks are released on B // Start another transaction on A: put(k, v2) with the same key // Check that the new transaction writes successfully // Allow the commit to proceed on C // Check that k=v2 everywhere StateSequencer sequencer = new StateSequencer(); sequencer.logicalThread( "tx1", "tx1:begin", "tx1:block_commit_on_backup", "tx1:after_rollback_on_primary", "tx1:after_rollback_on_backup", "tx1:resume_commit_on_backup", "tx1:after_commit_on_backup", "tx1:check"); sequencer.logicalThread("tx2", "tx2:begin", "tx2:end"); sequencer.order( "tx1:after_rollback_on_backup", "tx2:begin", "tx2:end", "tx1:resume_commit_on_backup"); advanceOnInterceptor( sequencer, cache(2), StateTransferInterceptor.class, matchCommand(CommitCommand.class).matchCount(0).build()) .before("tx1:block_commit_on_backup", "tx1:resume_commit_on_backup") .after("tx1:after_commit_on_backup"); advanceOnInterceptor( sequencer, cache(1), StateTransferInterceptor.class, matchCommand(RollbackCommand.class).build()) .after("tx1:after_rollback_on_primary"); advanceOnInterceptor( sequencer, cache(2), StateTransferInterceptor.class, matchCommand(RollbackCommand.class).build()) .after("tx1:after_rollback_on_backup"); assertEquals( Arrays.asList(address(1), address(2)), advancedCache(0).getDistributionManager().locate(TEST_KEY)); sequencer.advance("tx1:begin"); tm(0).begin(); cache(0).put(TEST_KEY, TX1_VALUE); try { tm(0).commit(); } catch (RollbackException e) { log.debugf("Commit timed out as expected", e); } sequencer.advance("tx2:begin"); LockManager lockManager1 = TestingUtil.extractLockManager(cache(1)); assertFalse(lockManager1.isLocked(TEST_KEY)); tm(0).begin(); cache(0).put(TEST_KEY, TX2_VALUE); tm(0).commit(); checkValue(); sequencer.advance("tx2:end"); sequencer.advance("tx1:check"); checkValue(); }
@Override public void accept( InvocationContext rCtx, VisitableCommand rCommand, Object rv, Throwable throwable) throws Throwable { lockManager.unlockAll(rCtx); }
protected final void lockAllAndRecord(InvocationContext context, Collection<?> keys, long timeout) throws InterruptedException { keys.forEach(context::addLockedKey); lockManager.lockAll(keys, context.getLockOwner(), timeout, TimeUnit.MILLISECONDS).lock(); }
protected final void lockAndRecord(InvocationContext context, Object key, long timeout) throws InterruptedException { context.addLockedKey(key); lockManager.lock(key, context.getLockOwner(), timeout, TimeUnit.MILLISECONDS).lock(); }
protected final Throwable cleanLocksAndRethrow(InvocationContext ctx, Throwable te) { lockManager.unlockAll(ctx); return te; }
private void lockAndWrap(InvocationContext ctx, Object key, InternalCacheEntry ice) throws InterruptedException { lockManager.acquireLock(ctx, key); entryFactory.wrapEntryForPut(ctx, key, ice, false); }