use of org.infinispan.container.entries.RepeatableReadEntry in project infinispan by infinispan.
the class ScatteredDistributionInterceptor method handleWriteManyOnOrigin.
private <C extends WriteCommand, Container, Item> Object handleWriteManyOnOrigin(InvocationContext ctx, C command, WriteManyCommandHelper<C, Container, Item> helper) {
LocalizedCacheTopology cacheTopology = checkTopology(command);
Map<Address, Container> remoteEntries = new HashMap<>();
for (Item item : helper.getItems(command)) {
Object key = helper.item2key(item);
DistributionInfo info = cacheTopology.getDistribution(key);
Address primary = info.primary();
if (primary == null) {
throw AllOwnersLostException.INSTANCE;
} else {
Container currentEntries = remoteEntries.computeIfAbsent(primary, k -> helper.newContainer());
helper.accumulate(currentEntries, item);
}
}
Object[] results = command.loadType() == DONT_LOAD ? null : new Object[command.getAffectedKeys().size()];
MergingCompletableFuture<Object> allFuture = new SyncMergingCompletableFuture<>(remoteEntries.size(), results, helper::transformResult);
int offset = 0;
Container localEntries = remoteEntries.remove(rpcManager.getAddress());
if (localEntries != null) {
helper.containerSize(localEntries);
C localCommand = helper.copyForLocal(command, localEntries);
localCommand.setTopologyId(command.getTopologyId());
LocalWriteManyHandler handler = new LocalWriteManyHandler(allFuture, localCommand.getAffectedKeys(), cacheTopology);
invokeNextAndFinally(ctx, localCommand, handler);
}
// This will be null in a non-biased variant
MultiTargetCollector multiTargetCollector = createMultiTargetCollector(command, remoteEntries.size());
for (Map.Entry<Address, Container> ownerEntry : remoteEntries.entrySet()) {
Address owner = ownerEntry.getKey();
// TODO: copyForLocal just creates the command with given entries, not using the segment-aware map
Container container = ownerEntry.getValue();
C toPrimary = helper.copyForLocal(command, container);
toPrimary.setTopologyId(command.getTopologyId());
CompletionStage<ValidResponse> rpcFuture = manyWriteOnRemotePrimary(owner, toPrimary, multiTargetCollector);
int myOffset = offset;
offset += helper.containerSize(container);
rpcFuture.whenComplete((response, t) -> {
if (t != null) {
allFuture.completeExceptionally(t);
return;
}
Object responseValue = response.getResponseValue();
// Note: we could use PrimaryResponseHandler, but we would have to add the reference to allFuture, offset...
InternalCacheValue[] values;
try {
if (command.loadType() == DONT_LOAD) {
if (!(responseValue instanceof InternalCacheValue[])) {
allFuture.completeExceptionally(new CacheException("Response from " + owner + ": expected InternalCacheValue[] but it is " + responseValue));
return;
}
values = (InternalCacheValue[]) responseValue;
} else {
if (!(responseValue instanceof Object[]) || (((Object[]) responseValue).length != 2)) {
allFuture.completeExceptionally(new CacheException("Response from " + owner + ": expected Object[2] but it is " + responseValue));
return;
}
// We use Object[] { InternalCacheValue[], Object[] } structure to get benefit of same-type array marshalling
// TODO optimize returning entry itself
// Note: some interceptors relying on the return value *could* have a problem interpreting this
values = (InternalCacheValue[]) ((Object[]) responseValue)[0];
MergingCompletableFuture.moveListItemsToFuture(((Object[]) responseValue)[1], allFuture, myOffset);
}
AggregateCompletionStage<Void> aggregateCompletionStage = CompletionStages.aggregateCompletionStage();
synchronized (allFuture) {
if (allFuture.isDone()) {
return;
}
int i = 0;
for (Object key : helper.toKeys(container)) {
// we will serve as the backup
InternalCacheEntry ice = values[i++].toInternalCacheEntry(key);
entryFactory.wrapExternalEntry(ctx, key, ice, true, true);
RepeatableReadEntry entry = (RepeatableReadEntry) ctx.lookupEntry(key);
// we don't care about setCreated() since backup owner should not fire listeners
entry.setChanged(true);
aggregateCompletionStage.dependsOn(commitSingleEntryIfNewer(entry, ctx, command));
if (entry.isCommitted() && !command.hasAnyFlag(FlagBitSets.PUT_FOR_STATE_TRANSFER)) {
scheduleKeyInvalidation(entry.getKey(), entry.getMetadata().version(), entry.isRemoved());
}
}
assert i == values.length;
}
aggregateCompletionStage.freeze().thenRun(allFuture::countDown);
} catch (Throwable t2) {
allFuture.completeExceptionally(t2);
}
});
}
return asyncValue(allFuture);
}
use of org.infinispan.container.entries.RepeatableReadEntry in project infinispan by infinispan.
the class EntryFactoryImpl method createWrappedEntry.
protected MVCCEntry<?, ?> createWrappedEntry(Object key, CacheEntry<?, ?> cacheEntry) {
Object value = null;
Metadata metadata = null;
PrivateMetadata internalMetadata = null;
if (cacheEntry != null) {
synchronized (cacheEntry) {
value = cacheEntry.getValue();
metadata = cacheEntry.getMetadata();
internalMetadata = cacheEntry.getInternalMetadata();
}
}
if (log.isTraceEnabled())
log.tracef("Creating new entry for key %s", toStr(key));
MVCCEntry<?, ?> mvccEntry;
if (useRepeatableRead) {
if (useVersioning) {
if (internalMetadata == null) {
internalMetadata = nonExistingPrivateMetadata;
}
mvccEntry = new VersionedRepeatableReadEntry(key, value, metadata);
} else {
mvccEntry = new RepeatableReadEntry(key, value, metadata);
}
} else {
mvccEntry = new ReadCommittedEntry(key, value, metadata);
}
mvccEntry.setInternalMetadata(internalMetadata);
if (cacheEntry != null) {
mvccEntry.setCreated(cacheEntry.getCreated());
mvccEntry.setLastUsed(cacheEntry.getLastUsed());
}
return mvccEntry;
}
use of org.infinispan.container.entries.RepeatableReadEntry in project infinispan by infinispan.
the class ScatteredDistributionInterceptor method handleWriteManyOnPrimary.
private Object handleWriteManyOnPrimary(InvocationContext ctx, WriteCommand cmd, Object rv) {
int numKeys = cmd.getAffectedKeys().size();
InternalCacheValue[] values = new InternalCacheValue[numKeys];
// keys are always iterated in order
int i = 0;
AggregateCompletionStage<Void> aggregateCompletionStage = CompletionStages.aggregateCompletionStage();
for (Object key : cmd.getAffectedKeys()) {
RepeatableReadEntry entry = (RepeatableReadEntry) ctx.lookupEntry(key);
EntryVersion nextVersion = svm.incrementVersion(keyPartitioner.getSegment(key));
entry.setMetadata(addVersion(entry.getMetadata(), nextVersion));
if (cmd.loadType() == DONT_LOAD) {
aggregateCompletionStage.dependsOn(commitSingleEntryIfNewer(entry, ctx, cmd));
} else {
aggregateCompletionStage.dependsOn(commitSingleEntryIfNoChange(entry, ctx, cmd));
}
values[i++] = new MetadataImmortalCacheValue(entry.getValue(), entry.getMetadata());
}
CompletionStage<Void> aggregatedStage = aggregateCompletionStage.freeze();
Object returnValue;
if (cmd.loadType() == DONT_LOAD) {
// Disable ignoring return value in response
cmd.setFlagsBitSet(cmd.getFlagsBitSet() & ~FlagBitSets.IGNORE_RETURN_VALUES);
returnValue = values;
} else {
returnValue = new Object[] { values, ((List) rv).toArray() };
}
return asyncValue(aggregatedStage).thenApply(ctx, cmd, ((rCtx, rCommand, rv1) -> manyWriteResponse(rCtx, cmd, returnValue)));
}
use of org.infinispan.container.entries.RepeatableReadEntry in project infinispan by infinispan.
the class ScatteredDistributionInterceptor method handleWriteCommand.
private Object handleWriteCommand(InvocationContext ctx, DataWriteCommand command) throws Throwable {
RepeatableReadEntry cacheEntry = (RepeatableReadEntry) ctx.lookupEntry(command.getKey());
EntryVersion seenVersion = getVersionOrNull(cacheEntry);
LocalizedCacheTopology cacheTopology = checkTopology(command);
DistributionInfo info = cacheTopology.getSegmentDistribution(command.getSegment());
if (info.primary() == null) {
throw OutdatedTopologyException.RETRY_NEXT_TOPOLOGY;
}
if (isLocalModeForced(command)) {
RepeatableReadEntry contextEntry = cacheEntry;
if (cacheEntry == null) {
entryFactory.wrapExternalEntry(ctx, command.getKey(), null, false, true);
contextEntry = (RepeatableReadEntry) ctx.lookupEntry(command.getKey());
}
EntryVersion nextVersion = null;
if (command.hasAnyFlag(FlagBitSets.PUT_FOR_STATE_TRANSFER)) {
// we don't increment versions with state transfer
} else if (info.isPrimary()) {
if (!cacheTopology.isConnected() && command instanceof MetadataAwareCommand) {
// Preload does not use functional commands which are not metadata-aware
Metadata metadata = ((MetadataAwareCommand) command).getMetadata();
svm.updatePreloadedEntryVersion(metadata.version());
} else {
// let's allow local-mode writes on primary owner, preserving versions
nextVersion = svm.incrementVersion(info.segmentId());
}
}
return commitSingleEntryOnReturn(ctx, command, contextEntry, nextVersion);
}
if (ctx.isOriginLocal()) {
if (info.isPrimary()) {
Object seenValue = cacheEntry.getValue();
return invokeNextThenApply(ctx, command, (rCtx, rCommand, rv) -> handleWriteOnOriginPrimary(rCtx, rCommand, rv, cacheEntry, seenValue, seenVersion, cacheTopology, info));
} else {
// not primary owner
CompletionStage<ValidResponse> rpcFuture = singleWriteOnRemotePrimary(info.primary(), command);
return asyncValue(rpcFuture).thenApply(ctx, command, handleWritePrimaryResponse);
}
} else {
// remote origin
if (info.isPrimary()) {
// TODO [ISPN-3918]: the previous value is unreliable as this could be second invocation
return invokeNextThenApply(ctx, command, (rCtx, cmd, rv) -> {
if (!cmd.isSuccessful()) {
if (log.isTraceEnabled())
log.tracef("Skipping the replication of the command as it did not succeed on primary owner (%s).", cmd);
return singleWriteResponse(rCtx, cmd, rv);
}
EntryVersion nextVersion = svm.incrementVersion(info.segmentId());
Metadata metadata = addVersion(cacheEntry.getMetadata(), nextVersion);
cacheEntry.setMetadata(metadata);
CompletionStage<Void> stage;
if (cmd.loadType() != DONT_LOAD) {
stage = commitSingleEntryIfNoChange(cacheEntry, rCtx, cmd);
} else {
stage = commitSingleEntryIfNewer(cacheEntry, rCtx, cmd);
}
Object returnValue = cmd.acceptVisitor(ctx, new PrimaryResponseGenerator(cacheEntry, rv));
return asyncValue(stage).thenApply(rCtx, cmd, (rCtx2, rCommand2, rv2) -> singleWriteResponse(rCtx, rCommand2, returnValue));
});
} else {
// The origin is primary and we're merely backup saving the data
assert cacheEntry == null || command.hasAnyFlag(FlagBitSets.SKIP_OWNERSHIP_CHECK);
RepeatableReadEntry contextEntry;
if (cacheEntry == null) {
entryFactory.wrapExternalEntry(ctx, command.getKey(), null, false, true);
contextEntry = (RepeatableReadEntry) ctx.lookupEntry(command.getKey());
} else {
contextEntry = cacheEntry;
}
return invokeNextThenApply(ctx, command, (rCtx, rCommand, rv) -> delayedNull(commitSingleEntryIfNewer(contextEntry, rCtx, rCommand)));
}
}
}
Aggregations