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; }
private void stopStateTransferIfNeeded(FlagAffectedCommand command) { if (command instanceof ClearCommand) { // If we are committing a ClearCommand now then no keys should be written by state transfer // from // now on until current rebalance ends. if (stateConsumer != null) { stateConsumer.stopApplyingState(); } if (xSiteStateConsumer != null) { xSiteStateConsumer.endStateTransfer(null); } } }