use of org.infinispan.commands.control.LockControlCommand in project infinispan by infinispan.
the class InfinispanNodeFailureTest method killedNodeDoesNotBreakReplaceCommand.
public void killedNodeDoesNotBreakReplaceCommand() throws Exception {
defineConfigurationOnAllManagers(TEST_CACHE, new ConfigurationBuilder().read(manager(0).getDefaultCacheConfiguration()));
waitForClusterToForm(TEST_CACHE);
waitForNoRebalance(caches(TEST_CACHE));
final Object replaceKey = new MagicKey("X", cache(0, TEST_CACHE));
final Object putKey = new MagicKey("Z", cache(1, TEST_CACHE));
cache(0, TEST_CACHE).put(replaceKey, INITIAL_VALUE);
// prepare third node to notify us when put command is in progress so we can kill the node
final CountDownLatch beforeKill = new CountDownLatch(1);
final CountDownLatch afterKill = new CountDownLatch(1);
advancedCache(1, TEST_CACHE).getAsyncInterceptorChain().addInterceptor(new BaseCustomAsyncInterceptor() {
@Override
public Object visitLockControlCommand(TxInvocationContext ctx, LockControlCommand command) throws Throwable {
return invokeNextAndFinally(ctx, command, (rCtx, rCommand, rv, t) -> {
LockControlCommand cmd = (LockControlCommand) rCommand;
if (putKey.equals(cmd.getSingleKey())) {
// notify main thread it can start killing third node
beforeKill.countDown();
// wait for completion and proceed
afterKill.await(10, TimeUnit.SECONDS);
}
});
}
}, 1);
// execute replace command in separate thread so we can do something else meanwhile
Future<Boolean> firstResult = fork(() -> {
try {
tm(0, TEST_CACHE).begin();
// this should replace and lock REPLACE_KEY so other transactions can't pass this barrier
boolean result = cache(0, TEST_CACHE).replace(replaceKey, INITIAL_VALUE, REPLACING_VALUE);
// issue put command so it is retried while node-c is being killed
cache(0, TEST_CACHE).put(putKey, "some-value");
// apply new view
viewLatch.countDown();
tm(0, TEST_CACHE).commit();
return result;
} catch (Throwable t) {
return null;
}
});
// wait third node to complete replace command and kill it
assertTrue(beforeKill.await(10, TimeUnit.SECONDS));
// kill node-c, do not wait rehash, it is important to continue with put-retry before new view is received
killMember(2, TEST_CACHE, false);
afterKill.countDown();
tm(1, TEST_CACHE).begin();
// this replace should never succeed because first node has already replaced and locked value
// but during put command replace lock is lost, so we can successfully replace the same value again, which is a bug
boolean secondResult = cache(1, TEST_CACHE).replace(replaceKey, INITIAL_VALUE, REPLACING_VALUE);
tm(1, TEST_CACHE).commit();
// check that first node did not fail
assertEquals(Boolean.TRUE, firstResult.get());
assertEquals(REPLACING_VALUE, cache(0, TEST_CACHE).get(replaceKey));
assertEquals(REPLACING_VALUE, cache(1, TEST_CACHE).get(replaceKey));
// check that second node state is inconsistent, second result should be FALSE in read committed pessimistic cache
// uncomment when this bug is fixed
assertEquals(false, secondResult);
}
use of org.infinispan.commands.control.LockControlCommand in project infinispan by infinispan.
the class CacheImpl method lock.
boolean lock(Collection<? extends K> keys, long flagsBitSet) {
if (!transactional)
throw new UnsupportedOperationException("Calling lock() on non-transactional caches is not allowed");
if (keys == null || keys.isEmpty()) {
throw new IllegalArgumentException("Cannot lock empty list of keys");
}
InvocationContext ctx = invocationContextFactory.createInvocationContext(true, UNBOUNDED);
LockControlCommand command = commandsFactory.buildLockControlCommand(keys, flagsBitSet);
if (ctx.getLockOwner() == null) {
ctx.setLockOwner(command.getKeyLockOwner());
}
return invocationHelper.invoke(ctx, command);
}
use of org.infinispan.commands.control.LockControlCommand in project infinispan by infinispan.
the class PessimisticLockingInterceptor method visitDataWriteCommand.
@Override
protected Object visitDataWriteCommand(InvocationContext ctx, DataWriteCommand command) throws Throwable {
Object maybeStage;
Object key = command.getKey();
if (hasSkipLocking(command)) {
// Non-modifying functional write commands are executed in non-transactional context on non-originators
if (ctx.isInTxScope()) {
// Mark the key as affected even with SKIP_LOCKING
((TxInvocationContext<?>) ctx).addAffectedKey(key);
}
maybeStage = invokeNext(ctx, command);
} else {
if (!needRemoteLocks(ctx, key, command)) {
maybeStage = acquireLocalLockAndInvokeNext(ctx, command);
} else {
final TxInvocationContext txContext = (TxInvocationContext) ctx;
LockControlCommand lcc = cf.buildLockControlCommand(key, command.getFlagsBitSet(), txContext.getGlobalTransaction());
lcc.setTopologyId(command.getTopologyId());
// the chain again with the actual command
return invokeNextThenApply(ctx, lcc, (rCtx, rCommand, rv) -> acquireLocalLockAndInvokeNext(rCtx, command));
}
}
return maybeStage;
}
use of org.infinispan.commands.control.LockControlCommand in project infinispan by infinispan.
the class AsyncInterceptorChainInvocationTest method testInvokeNextSubCommand.
public void testInvokeNextSubCommand() {
AsyncInterceptorChain chain = newInterceptorChain(new BaseAsyncInterceptor() {
@Override
public Object visitCommand(InvocationContext ctx, VisitableCommand command) throws Throwable {
return invokeNext(ctx, testSubCommand);
}
}, new BaseAsyncInterceptor() {
@Override
public Object visitCommand(InvocationContext ctx, VisitableCommand command) throws Throwable {
return command instanceof LockControlCommand ? "subCommand" : "command";
}
});
InvocationContext context = newInvocationContext();
Object returnValue = chain.invoke(context, testCommand);
assertEquals("subCommand", returnValue);
}
use of org.infinispan.commands.control.LockControlCommand in project infinispan by infinispan.
the class AsyncInterceptorChainInvocationTest method testInvokeNextAsyncSubCommand.
public void testInvokeNextAsyncSubCommand() throws Exception {
CompletableFuture<Object> f = new CompletableFuture<>();
AsyncInterceptorChain chain = newInterceptorChain(new BaseAsyncInterceptor() {
@Override
public Object visitCommand(InvocationContext ctx, VisitableCommand command) throws Throwable {
return asyncInvokeNext(ctx, testSubCommand, f);
}
}, new BaseAsyncInterceptor() {
@Override
public Object visitCommand(InvocationContext ctx, VisitableCommand command) throws Throwable {
return command instanceof LockControlCommand ? "subCommand" : "command";
}
});
InvocationContext context = newInvocationContext();
CompletableFuture<Object> invokeFuture = chain.invokeAsync(context, testCommand);
assertFalse(invokeFuture.isDone());
f.complete("v");
assertEquals("subCommand", invokeFuture.get(10, SECONDS));
}
Aggregations