use of org.infinispan.commands.write.ValueMatcher in project infinispan by infinispan.
the class CallInterceptor method visitReadWriteKeyValueCommand.
@Override
public Object visitReadWriteKeyValueCommand(InvocationContext ctx, ReadWriteKeyValueCommand command) throws Throwable {
ValueMatcher valueMatcher = command.getValueMatcher();
// It's not worth looking up the entry if we're never going to apply the change.
if (valueMatcher == ValueMatcher.MATCH_NEVER) {
command.fail();
return null;
}
MVCCEntry e = (MVCCEntry) ctx.lookupEntry(command.getKey());
// Could be that the key is not local
if (e == null)
return null;
Object prevValue = command.getPrevValue();
Metadata prevMetadata = command.getPrevMetadata();
boolean hasCommandRetry = command.hasAnyFlag(FlagBitSets.COMMAND_RETRY);
// Command only has one previous value, do not override it
if (prevValue == null && !hasCommandRetry) {
prevValue = e.getValue();
prevMetadata = e.getMetadata();
command.setPrevValueAndMetadata(prevValue, prevMetadata);
}
// Protect against outdated old value using the value matcher.
// If the value has been update while on the retry, use the newer value.
// Also take into account that the value might have been removed.
// TODO: Configure equivalence function
// TODO: this won't work properly until we store if the command was executed or not...
Object oldPrevValue = e.getValue();
// Note: other commands don't clone the entry as they don't carry the previous value for comparison
// using value matcher - if other commands are retried these can apply the function multiple times.
// Here we don't want to modify the value in context when trying what would be the outcome of the operation.
MVCCEntry copy = e.clone();
DataConversion valueDataConversion = command.getValueDataConversion();
Object decodedArgument = valueDataConversion.fromStorage(command.getArgument());
EntryViews.AccessLoggingReadWriteView view = EntryViews.readWrite(copy, prevValue, prevMetadata, command.getKeyDataConversion(), valueDataConversion);
Object ret = snapshot(command.getBiFunction().apply(decodedArgument, view));
if (valueMatcher.matches(oldPrevValue, prevValue, copy.getValue())) {
log.tracef("Execute read-write function on previous value %s and previous metadata %s", prevValue, prevMetadata);
e.setValue(copy.getValue());
e.setMetadata(copy.getMetadata());
// These are the only flags that should be changed with EntryViews.readWrite
e.setChanged(copy.isChanged());
e.setRemoved(copy.isRemoved());
}
// The effective result of retried command is not safe; we'll go to backup anyway
if (!e.isChanged() && !hasCommandRetry) {
command.fail();
}
updateStoreFlags(command, e);
return Param.StatisticsMode.isSkip(command.getParams()) ? ret : StatsEnvelope.create(ret, e, prevValue != null, view.isRead());
}
use of org.infinispan.commands.write.ValueMatcher in project infinispan by infinispan.
the class CallInterceptor method visitPutKeyValueCommand.
@Override
public Object visitPutKeyValueCommand(InvocationContext ctx, PutKeyValueCommand command) throws Throwable {
// It's not worth looking up the entry if we're never going to apply the change.
ValueMatcher valueMatcher = command.getValueMatcher();
if (valueMatcher == ValueMatcher.MATCH_NEVER) {
command.fail();
return null;
}
// noinspection unchecked
Object key = command.getKey();
MVCCEntry<Object, Object> e = (MVCCEntry) ctx.lookupEntry(key);
if (e == null) {
throw new IllegalStateException("Not wrapped");
}
Object newValue = command.getValue();
Metadata metadata = command.getMetadata();
if (metadata instanceof InternalMetadataImpl) {
InternalMetadataImpl internalMetadata = (InternalMetadataImpl) metadata;
metadata = internalMetadata.actual();
e.setCreated(internalMetadata.created());
e.setLastUsed(internalMetadata.lastUsed());
}
Object prevValue = e.getValue();
if (!valueMatcher.matches(prevValue, null, newValue)) {
command.fail();
return prevValue;
}
return performPut(e, ctx, valueMatcher, key, newValue, metadata, command, command.hasAnyFlag(FlagBitSets.PUT_FOR_STATE_TRANSFER | FlagBitSets.PUT_FOR_X_SITE_STATE_TRANSFER));
}
use of org.infinispan.commands.write.ValueMatcher in project infinispan by infinispan.
the class CallInterceptor method visitRemoveExpiredCommand.
@Override
public Object visitRemoveExpiredCommand(InvocationContext ctx, RemoveExpiredCommand command) throws Throwable {
Object key = command.getKey();
MVCCEntry e = (MVCCEntry) ctx.lookupEntry(key);
Metadata metadata = command.getMetadata();
// When it isn't the primary owner, just accept the removal as is, trusting the primary did the appropriate checks
if (command.hasAnyFlag(FlagBitSets.BACKUP_WRITE)) {
if (log.isTraceEnabled()) {
log.trace("Removing expired entry without checks as we are backup as primary already performed them");
}
e.setExpired(true);
return performRemove(e, ctx, ValueMatcher.MATCH_ALWAYS, key, e.getValue() != null ? e.getValue() : null, command.getValue(), metadata, false, command);
}
if (e != null && !e.isRemoved()) {
Object prevValue = e.getValue();
Object optionalValue = command.getValue();
Long lifespan = command.getLifespan();
ValueMatcher valueMatcher = command.getValueMatcher();
// We also skip checks if lifespan is null as this is a max idle expiration
if (lifespan == null || command.hasAnyFlag(FlagBitSets.SKIP_SHARED_CACHE_STORE)) {
if (valueMatcher.matches(prevValue, optionalValue, null)) {
e.setExpired(true);
return performRemove(e, ctx, valueMatcher, key, prevValue, optionalValue, metadata, false, command);
}
} else if (versionFromEntry(e) == nonExistentVersion) {
// If we have a value though we should verify it matches the value as well
if (optionalValue == null || valueMatcher.matches(prevValue, optionalValue, null)) {
e.setExpired(true);
return performRemove(e, ctx, valueMatcher, key, prevValue, optionalValue, metadata, false, command);
}
} else if (e.getLifespan() > 0 && e.getLifespan() == lifespan) {
// Lastly if there is metadata we have to verify it equals our lifespan and the value match.
if (valueMatcher.matches(prevValue, optionalValue, null)) {
// expire should give a good enough buffer range to not create a false positive.
if (ExpiryHelper.isExpiredMortal(lifespan, e.getCreated(), timeService.wallClockTime() + CLOCK_BUFFER)) {
if (log.isTraceEnabled()) {
log.tracef("Removing entry as its lifespan and value match and it created on %s with a current time of %s", e.getCreated(), timeService.wallClockTime());
}
e.setExpired(true);
return performRemove(e, ctx, valueMatcher, key, prevValue, optionalValue, metadata, false, command);
} else if (log.isTraceEnabled()) {
log.tracef("Cannot remove entry due to it not being expired - this can be caused by different " + "clocks on nodes or a concurrent write");
}
} else if (log.isTraceEnabled()) {
log.tracef("Cannot remove entry due to the value not being equal. Matcher: %s, PrevValue: %s, ExpectedValue: %s. Double check equality is working for the value", valueMatcher, prevValue, optionalValue);
}
} else if (log.isTraceEnabled()) {
log.trace("Cannot remove entry as its lifespan or value do not match");
}
} else {
if (log.isTraceEnabled()) {
log.trace("Nothing to remove since the entry doesn't exist in the context or it is already removed - assume command was successful");
}
return true;
}
command.fail();
return false;
}
use of org.infinispan.commands.write.ValueMatcher in project infinispan by infinispan.
the class CallInterceptor method visitReplaceCommand.
@Override
public Object visitReplaceCommand(InvocationContext ctx, ReplaceCommand command) throws Throwable {
ValueMatcher valueMatcher = command.getValueMatcher();
// It's not worth looking up the entry if we're never going to apply the change.
if (valueMatcher == ValueMatcher.MATCH_NEVER) {
command.fail();
// TODO: this seems like a bug.. ?
return null;
}
Object key = command.getKey();
// noinspection unchecked
MVCCEntry<Object, Object> e = (MVCCEntry) ctx.lookupEntry(key);
// We need the null check as in non-tx caches we don't always wrap the entry on the origin
Object prevValue = e.getValue();
Object newValue = command.getNewValue();
Object expectedValue = command.getOldValue();
if (valueMatcher.matches(prevValue, expectedValue, newValue)) {
e.setChanged(true);
e.setValue(newValue);
Metadata newMetadata = command.getMetadata();
Metadata prevMetadata = e.getMetadata();
CompletionStage<Void> stage = cacheNotifier.notifyCacheEntryModified(key, newValue, newMetadata, expectedValue == null ? prevValue : expectedValue, prevMetadata, true, ctx, command);
Metadatas.updateMetadata(e, newMetadata);
updateStoreFlags(command, e);
return delayedValue(stage, expectedValue == null ? prevValue : true);
}
command.fail();
return expectedValue == null ? prevValue : false;
}
use of org.infinispan.commands.write.ValueMatcher in project infinispan by infinispan.
the class BaseDistributionInterceptor method primaryReturnHandler.
protected Object primaryReturnHandler(InvocationContext ctx, AbstractDataWriteCommand command, Object localResult) {
if (!command.isSuccessful()) {
if (log.isTraceEnabled())
log.tracef("Skipping the replication of the conditional command as it did not succeed on primary owner (%s).", command);
return localResult;
}
LocalizedCacheTopology cacheTopology = checkTopologyId(command);
int segment = SegmentSpecificCommand.extractSegment(command, command.getKey(), keyPartitioner);
DistributionInfo distributionInfo = cacheTopology.getSegmentDistribution(segment);
Collection<Address> owners = distributionInfo.writeOwners();
if (owners.size() == 1) {
// There are no backups, skip the replication part.
return localResult;
}
// Cache the matcher and reset it if we get OOTE (or any other exception) from backup
ValueMatcher originalMatcher = command.getValueMatcher();
// Ignore the previous value on the backup owners
command.setValueMatcher(ValueMatcher.MATCH_ALWAYS);
if (!isSynchronous(command)) {
if (isReplicated) {
rpcManager.sendToAll(command, DeliverOrder.PER_SENDER);
} else {
rpcManager.sendToMany(owners, command, DeliverOrder.PER_SENDER);
}
// Switch to the retry policy, in case the primary owner changes before we commit locally
command.setValueMatcher(originalMatcher.matcherForRetry());
return localResult;
}
VoidResponseCollector collector = VoidResponseCollector.ignoreLeavers();
RpcOptions rpcOptions = rpcManager.getSyncRpcOptions();
// Mark the command as a backup write so it can skip some checks
command.addFlags(FlagBitSets.BACKUP_WRITE);
CompletionStage<Void> remoteInvocation = isReplicated ? rpcManager.invokeCommandOnAll(command, collector, rpcOptions) : rpcManager.invokeCommand(owners, command, collector, rpcOptions);
return asyncValue(remoteInvocation.handle((ignored, t) -> {
// Unset the backup write bit as the command will be retried
command.setFlagsBitSet(command.getFlagsBitSet() & ~FlagBitSets.BACKUP_WRITE);
// Switch to the retry policy, in case the primary owner changed and the write already succeeded on the new primary
command.setValueMatcher(originalMatcher.matcherForRetry());
CompletableFutures.rethrowExceptionIfPresent(t);
return localResult;
}));
}
Aggregations