@Override public CompletableFuture<Void> visitReadOnlyKeyCommand( InvocationContext ctx, ReadOnlyKeyCommand command) throws Throwable { try { CacheEntry entry = entryFactory.wrapEntryForReading(ctx, command.getKey(), null); // Null entry is often considered to mean that entry is not available // locally, but if there's no need to get remote, the read-only // function needs to be executed, so force a non-null entry in // context with null content if (entry == null && cdl.localNodeIsOwner(command.getKey())) { entryFactory.wrapEntryForReading(ctx, command.getKey(), NullCacheEntry.getInstance()); } return ctx.shortCircuit(ctx.forkInvocationSync(command)); } finally { // needed because entries might be added in L1 if (!ctx.isInTxScope()) commitContextEntries(ctx, command, null); else { CacheEntry entry = ctx.lookupEntry(command.getKey()); if (entry != null) { entry.setSkipLookup(true); } } } }
@Override public Object visitApplyDeltaCommand(InvocationContext ctx, ApplyDeltaCommand command) throws Throwable { if (cdl.localNodeIsOwner(command.getKey())) { entryFactory.wrapEntryForDelta(ctx, command.getKey(), command.getDelta()); ctx.forkInvocationSync(command); } return null; }
private Object invokeNextAndApplyChanges( InvocationContext ctx, FlagAffectedCommand command, Metadata metadata) throws Throwable { final Object result = ctx.forkInvocationSync(command); if (!ctx.isInTxScope()) { stateTransferLock.acquireSharedTopologyLock(); try { // We only retry non-tx write commands if (command instanceof WriteCommand) { WriteCommand writeCommand = (WriteCommand) command; // Can't perform the check during preload or if the cache isn't clustered boolean isSync = (cacheConfiguration.clustering().cacheMode().isSynchronous() && !command.hasFlag(Flag.FORCE_ASYNCHRONOUS)) || command.hasFlag(Flag.FORCE_SYNCHRONOUS); if (writeCommand.isSuccessful() && stateConsumer != null && stateConsumer.getCacheTopology() != null) { int commandTopologyId = command.getTopologyId(); int currentTopologyId = stateConsumer.getCacheTopology().getTopologyId(); // TotalOrderStateTransferInterceptor doesn't set the topology id for PFERs. if (isSync && currentTopologyId != commandTopologyId && commandTopologyId != -1) { // If we were the originator of a data command which we didn't own the key at the time // means it // was already committed, so there is no need to throw the OutdatedTopologyException // This will happen if we submit a command to the primary owner and it responds and // then a topology // change happens before we get here if (!ctx.isOriginLocal() || !(command instanceof DataCommand) || ctx.hasLockedKey(((DataCommand) command).getKey())) { if (trace) log.tracef( "Cache topology changed while the command was executing: expected %d, got %d", commandTopologyId, currentTopologyId); // This shouldn't be necessary, as we'll have a fresh command instance when retrying writeCommand.setValueMatcher(writeCommand.getValueMatcher().matcherForRetry()); throw new OutdatedTopologyException( "Cache topology changed while the command was executing: expected " + commandTopologyId + ", got " + currentTopologyId); } } } } commitContextEntries(ctx, command, metadata); } finally { stateTransferLock.releaseSharedTopologyLock(); } } if (trace) log.tracef("The return value is %s", toStr(result)); return result; }
@Override public Object visitPutKeyValueCommand(InvocationContext ctx, PutKeyValueCommand command) throws Throwable { if (cdl.localNodeIsOwner(command.getKey())) { entryFactory.wrapEntryForWriting( ctx, command.getKey(), EntryFactory.Wrap.WRAP_ALL, false, false); ctx.forkInvocationSync(command); } return null; }
@Override public Object visitRemoveCommand(InvocationContext ctx, RemoveCommand command) throws Throwable { if (cdl.localNodeIsOwner(command.getKey())) { boolean forceWrap = command.getValueMatcher().nonExistentEntryCanMatch(); EntryFactory.Wrap wrap = forceWrap ? EntryFactory.Wrap.WRAP_ALL : EntryFactory.Wrap.WRAP_NON_NULL; entryFactory.wrapEntryForWriting(ctx, command.getKey(), wrap, false, false); ctx.forkInvocationSync(command); } return null; }
@Override public Object visitReplaceCommand(InvocationContext ctx, ReplaceCommand command) throws Throwable { if (cdl.localNodeIsOwner(command.getKey())) { // When retrying, we need to perform the command even if the previous value was deleted. boolean forceWrap = command.getValueMatcher().nonExistentEntryCanMatch(); EntryFactory.Wrap wrap = forceWrap ? EntryFactory.Wrap.WRAP_ALL : EntryFactory.Wrap.WRAP_NON_NULL; entryFactory.wrapEntryForWriting(ctx, command.getKey(), wrap, false, false); ctx.forkInvocationSync(command); } return null; }
@Override public Object visitInvalidateCommand(InvocationContext ctx, InvalidateCommand command) throws Throwable { if (command.getKeys() != null) { for (Object key : command.getKeys()) { if (cdl.localNodeIsOwner(key)) { entryFactory.wrapEntryForWriting( ctx, key, EntryFactory.Wrap.WRAP_NON_NULL, false, false); ctx.forkInvocationSync(command); } } } return null; }
private CompletableFuture<Void> visitDataReadCommand( InvocationContext ctx, AbstractDataCommand command) throws Throwable { try { entryFactory.wrapEntryForReading(ctx, command.getKey(), null); return ctx.shortCircuit(ctx.forkInvocationSync(command)); } finally { // needed because entries might be added in L1 if (!ctx.isInTxScope()) commitContextEntries(ctx, command, null); else { CacheEntry entry = ctx.lookupEntry(command.getKey()); if (entry != null) { entry.setSkipLookup(true); } } } }
@Override public Object visitPutMapCommand(InvocationContext ctx, PutMapCommand command) throws Throwable { Map<Object, Object> newMap = new HashMap<>(4); for (Map.Entry<Object, Object> e : command.getMap().entrySet()) { Object key = e.getKey(); if (cdl.localNodeIsOwner(key)) { entryFactory.wrapEntryForWriting(ctx, key, EntryFactory.Wrap.WRAP_ALL, true, false); newMap.put(key, e.getValue()); } } if (newMap.size() > 0) { PutMapCommand clonedCommand = commandFactory.buildPutMapCommand( newMap, command.getMetadata(), command.getFlagsBitSet()); ctx.forkInvocationSync(clonedCommand); } return null; }
@Override public CompletableFuture<Void> visitReadOnlyManyCommand( InvocationContext ctx, ReadOnlyManyCommand command) throws Throwable { try { for (Object key : command.getKeys()) { entryFactory.wrapEntryForReading(ctx, key, null); } return ctx.shortCircuit(ctx.forkInvocationSync(command)); } finally { if (ctx.isInTxScope()) { for (Object key : command.getKeys()) { CacheEntry entry = ctx.lookupEntry(key); if (entry != null) { entry.setSkipLookup(true); } } } } }