private void raiseEventForInitialTransfer(UUID identifier, CacheEntry entry, boolean clustered) { EventImpl preEvent; if (clustered) { // In clustered mode we only send post event preEvent = null; } else { preEvent = EventImpl.createEvent(cache, CACHE_ENTRY_CREATED); preEvent.setKey(entry.getKey()); preEvent.setPre(true); } EventImpl postEvent = EventImpl.createEvent(cache, CACHE_ENTRY_CREATED); postEvent.setKey(entry.getKey()); postEvent.setValue(entry.getValue()); postEvent.setMetadata(entry.getMetadata()); postEvent.setPre(false); for (CacheEntryListenerInvocation<K, V> invocation : cacheEntryCreatedListeners) { // Now notify all our methods of the creates if (invocation.getIdentifier() == identifier) { if (preEvent != null) { // Non clustered notifications are done twice invocation.invokeNoChecks(preEvent, true, true); } invocation.invokeNoChecks(postEvent, true, true); } } }
private void configureEvent( EventImpl<K, V> e, K key, V value, boolean pre, InvocationContext ctx, FlagAffectedCommand command, V previousValue, Metadata previousMetadata) { boolean originLocal = ctx.isOriginLocal(); e.setOriginLocal(originLocal); e.setValue(pre ? previousValue : value); e.setPre(pre); e.setOldValue(previousValue); e.setOldMetadata(previousMetadata); CacheEntry entry = ctx.lookupEntry(key); if (entry != null) { e.setMetadata(entry.getMetadata()); } Set<Flag> flags; if (command != null && (flags = command.getFlags()) != null && flags.contains(Flag.COMMAND_RETRY)) { e.setCommandRetried(true); } e.setKey(key); setTx(ctx, e); }
private boolean needsRemoteGet(InvocationContext ctx, Object key, boolean retvalCheck) { final CacheEntry entry; return retvalCheck && !ctx.hasFlag(Flag.CACHE_MODE_LOCAL) && !ctx.hasFlag(Flag.SKIP_REMOTE_LOOKUP) && ((entry = ctx.lookupEntry(key)) == null || entry.isNull() || entry.isLockPlaceholder()); }
@Override public final CacheEntry wrapEntryForReading(InvocationContext ctx, Object key) throws InterruptedException { CacheEntry cacheEntry = getFromContext(ctx, key); if (cacheEntry == null) { cacheEntry = getFromContainer(key); // do not bother wrapping though if this is not in a tx. repeatable read etc are all // meaningless unless there is a tx. if (useRepeatableRead) { MVCCEntry mvccEntry = cacheEntry == null ? createWrappedEntry(key, null, null, false, false, -1) : createWrappedEntry( key, cacheEntry.getValue(), cacheEntry.getVersion(), false, false, cacheEntry.getLifespan()); if (mvccEntry != null) ctx.putLookedUpEntry(key, mvccEntry); return mvccEntry; } else if (cacheEntry != null) { // if not in transaction and repeatable read, or simply read committed // (regardless of whether in TX or not), do not wrap ctx.putLookedUpEntry(key, cacheEntry); } return cacheEntry; } return cacheEntry; }
@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 protected void commitSingleEntry( CacheEntry entry, Metadata metadata, FlagAffectedCommand command, InvocationContext ctx, Flag trackFlag, boolean l1Invalidation) { // Cache flags before they're reset // TODO: Can the reset be done after notification instead? boolean created = entry.isCreated(); boolean removed = entry.isRemoved(); boolean expired; if (removed && entry instanceof MVCCEntry) { expired = ((MVCCEntry) entry).isExpired(); } else { expired = false; } InternalCacheEntry previousEntry = dataContainer.peek(entry.getKey()); Object previousValue = null; Metadata previousMetadata = null; if (previousEntry != null) { previousValue = previousEntry.getValue(); previousMetadata = previousEntry.getMetadata(); } commitManager.commit(entry, metadata, trackFlag, l1Invalidation); // Notify after events if necessary notifyCommitEntry( created, removed, expired, entry, ctx, command, previousValue, previousMetadata); }
public static Collection getInternalValues(Cache cache) { DataContainer dataContainer = TestingUtil.extractComponent(cache, DataContainer.class); Collection values = new ArrayList(); for (CacheEntry entry : dataContainer) { values.add(entry.getValue()); } return values; }
public static Set getInternalKeys(Cache cache) { DataContainer dataContainer = TestingUtil.extractComponent(cache, DataContainer.class); Set keys = new HashSet(); for (CacheEntry entry : dataContainer) { keys.add(entry.getKey()); } return keys; }
/** * Locks the value for the keys accessed by the command to avoid being override from a remote get. */ private CompletableFuture<Void> setSkipRemoteGetsAndInvokeNextForDataCommand( InvocationContext context, DataWriteCommand command, Metadata metadata) throws Throwable { Object retVal = invokeNextAndApplyChanges(context, command, metadata); if (context.isInTxScope()) { CacheEntry entry = context.lookupEntry(command.getKey()); if (entry != null) { entry.setSkipLookup(true); } } return context.shortCircuit(retVal); }
private DeltaAwareCacheEntry wrapInternalCacheEntryForDelta( InvocationContext ctx, Object key, CacheEntry cacheEntry) { DeltaAwareCacheEntry e; if (cacheEntry instanceof MVCCEntry) { e = createWrappedDeltaEntry(key, (DeltaAware) cacheEntry.getValue(), cacheEntry); } else { e = createWrappedDeltaEntry(key, (DeltaAware) cacheEntry.getValue(), null); } ctx.putLookedUpEntry(key, e); return e; }
public static String printCache(Cache cache) { DataContainer dataContainer = TestingUtil.extractComponent(cache, DataContainer.class); Iterator it = dataContainer.iterator(); StringBuilder builder = new StringBuilder(cache.getName() + "["); while (it.hasNext()) { CacheEntry ce = (CacheEntry) it.next(); builder.append(ce.getKey() + "=" + ce.getValue() + ",l=" + ce.getLifespan() + "; "); } builder.append("]"); return builder.toString(); }
@Override public Object perform(InvocationContext ctx) throws Throwable { CacheEntry entry = ctx.lookupEntry(key); if (entry == null || entry.isNull()) { return null; } if (entry.isRemoved()) { return null; } return entryFactory.copy(entry); }
private MVCCEntry wrapMvccEntryForRemove( InvocationContext ctx, Object key, CacheEntry cacheEntry) { MVCCEntry mvccEntry = createWrappedEntry( key, cacheEntry.getValue(), cacheEntry.getVersion(), false, true, cacheEntry.getLifespan()); ctx.putLookedUpEntry(key, mvccEntry); return mvccEntry; }
/** * Locks the value for the keys accessed by the command to avoid being override from a remote get. */ private CompletableFuture<Void> setSkipRemoteGetsAndInvokeNextForPutMapCommand( InvocationContext context, WriteCommand command) throws Throwable { Object retVal = invokeNextAndApplyChanges(context, command, command.getMetadata()); if (context.isInTxScope()) { for (Object key : command.getAffectedKeys()) { CacheEntry entry = context.lookupEntry(key); if (entry != null) { entry.setSkipLookup(true); } } } return context.shortCircuit(retVal); }
public void testWriteLockIsAcquired() throws Exception { advancedCache.put("k", "v"); assertNotLocked(advancedCache, "k"); tm.begin(); advancedCache.withFlags(Flag.FORCE_WRITE_LOCK).get("k"); InvocationContext ic = advancedCache.getInvocationContextContainer().getInvocationContext(true); CacheEntry cacheEntry = ic.getLookedUpEntries().get("k"); assert (cacheEntry instanceof ReadCommittedEntry && cacheEntry.isChanged()); assertLocked(advancedCache, "k"); tm.commit(); assertNotLocked(advancedCache, "k"); }
protected Object performRemove(CacheEntry e, InvocationContext ctx) { final Object removedValue = e.getValue(); notify(ctx, removedValue, e.getMetadata(), true); e.setRemoved(true); e.setValid(false); e.setChanged(true); if (valueMatcher != ValueMatcher.MATCH_EXPECTED_OR_NEW) { return isConditional() ? true : removedValue; } else { // Return the expected value when retrying return isConditional() ? true : value; } }
@Override protected void commitContextEntry( CacheEntry entry, InvocationContext ctx, FlagAffectedCommand command) { if (ctx.isInTxScope() && !isFromStateTransfer(ctx)) { EntryVersion version = ((TxInvocationContext) ctx) .getCacheTransaction() .getUpdatedEntryVersions() .get(entry.getKey()); cdl.commitEntry(entry, version, command, ctx); } else { // This could be a state transfer call! cdl.commitEntry(entry, entry.getVersion(), command, ctx); } }
@Override protected void commitContextEntry( CacheEntry entry, InvocationContext ctx, FlagAffectedCommand command, Metadata metadata, Flag stateTransferFlag, boolean l1Invalidation) { if (ctx.isInTxScope() && stateTransferFlag == null) { Metadata commitMetadata; // If user provided version, use it, otherwise generate/increment accordingly ClusteredRepeatableReadEntry clusterMvccEntry = (ClusteredRepeatableReadEntry) entry; EntryVersion existingVersion = clusterMvccEntry.getMetadata().version(); EntryVersion newVersion; if (existingVersion == null) { newVersion = versionGenerator.generateNew(); } else { newVersion = versionGenerator.increment((IncrementableEntryVersion) existingVersion); } if (metadata == null) commitMetadata = new EmbeddedMetadata.Builder().version(newVersion).build(); else commitMetadata = metadata.builder().version(newVersion).build(); cdl.commitEntry(entry, commitMetadata, command, ctx, null, l1Invalidation); } else { // This could be a state transfer call! cdl.commitEntry(entry, entry.getMetadata(), command, ctx, stateTransferFlag, l1Invalidation); } }
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); } } } }
/** * It tries to commit the cache entry. The entry is not committed if it is originated from state * transfer and other operation already has updated it. * * @param entry the entry to commit * @param metadata the entry's metadata * @param operation if {@code null}, it identifies this commit as originated from a normal * operation. Otherwise, it is originated from a state transfer (local or remote site) */ public final void commit( final CacheEntry entry, final Metadata metadata, final Flag operation, boolean l1Invalidation) { if (trace) { log.tracef( "Trying to commit. Key=%s. Operation Flag=%s, L1 invalidation=%s", toStr(entry.getKey()), operation, l1Invalidation); } if (l1Invalidation || (operation == null && !trackStateTransfer && !trackXSiteStateTransfer)) { // track == null means that it is a normal put and the tracking is not enabled! // if it is a L1 invalidation, commit without track it. if (trace) { log.tracef( "Committing key=%s. It is a L1 invalidation or a normal put and no tracking is enabled!", toStr(entry.getKey())); } entry.commit(dataContainer, metadata); return; } if (isTrackDisabled(operation)) { // this a put for state transfer but we are not tracking it. This means that the state // transfer has ended // or canceled due to a clear command. if (trace) { log.tracef( "Not committing key=%s. It is a state transfer key but no track is enabled!", toStr(entry.getKey())); } return; } tracker.compute( entry.getKey(), (o, discardPolicy) -> { if (discardPolicy != null && discardPolicy.ignore(operation)) { if (trace) { log.tracef( "Not committing key=%s. It was already overwritten! Discard policy=%s", toStr(entry.getKey()), discardPolicy); } return discardPolicy; } entry.commit(dataContainer, metadata); DiscardPolicy newDiscardPolicy = calculateDiscardPolicy(); if (trace) { log.tracef( "Committed key=%s. Old discard policy=%s. New discard policy=%s", toStr(entry.getKey()), discardPolicy, newDiscardPolicy); } return newDiscardPolicy; }); }
@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); } } } } }
@Override public final MVCCEntry wrapEntryForPut( InvocationContext ctx, Object key, InternalCacheEntry icEntry, boolean undeleteIfNeeded) throws InterruptedException { CacheEntry cacheEntry = getFromContext(ctx, key); MVCCEntry mvccEntry; if (cacheEntry != null && cacheEntry.isNull()) cacheEntry = null; if (cacheEntry != null) { mvccEntry = wrapMvccEntryForPut(ctx, key, cacheEntry); mvccEntry.undelete(undeleteIfNeeded); } else { InternalCacheEntry ice = (icEntry == null ? getFromContainer(key) : icEntry); // A putForExternalRead is putIfAbsent, so if key present, do nothing if (ice != null && ctx.hasFlag(Flag.PUT_FOR_EXTERNAL_READ)) return null; mvccEntry = ice != null ? wrapInternalCacheEntryForPut(ctx, key, ice) : newMvccEntryForPut(ctx, key); } mvccEntry.copyForUpdate(container, localModeWriteSkewCheck); return mvccEntry; }
private Object realRemoteGet( InvocationContext ctx, Object key, boolean storeInL1, boolean isWrite) throws Throwable { if (trace) log.tracef("Doing a remote get for key %s", key); boolean acquireRemoteLock = false; if (ctx.isInTxScope()) { TxInvocationContext txContext = (TxInvocationContext) ctx; acquireRemoteLock = isWrite && isPessimisticCache && !txContext.getAffectedKeys().contains(key); } // attempt a remote lookup InternalCacheEntry ice = dm.retrieveFromRemoteSource(key, ctx, acquireRemoteLock); if (acquireRemoteLock) { ((TxInvocationContext) ctx).addAffectedKey(key); } if (ice != null) { if (storeInL1) { if (isL1CacheEnabled) { if (trace) log.tracef("Caching remotely retrieved entry for key %s in L1", key); // This should be fail-safe try { long lifespan = ice.getLifespan() < 0 ? configuration.getL1Lifespan() : Math.min(ice.getLifespan(), configuration.getL1Lifespan()); PutKeyValueCommand put = cf.buildPutKeyValueCommand( ice.getKey(), ice.getValue(), lifespan, -1, ctx.getFlags()); lockAndWrap(ctx, key, ice); invokeNextInterceptor(ctx, put); } catch (Exception e) { // Couldn't store in L1 for some reason. But don't fail the transaction! log.infof("Unable to store entry %s in L1 cache", key); log.debug("Inability to store in L1 caused by", e); } } else { CacheEntry ce = ctx.lookupEntry(key); if (ce == null || ce.isNull() || ce.isLockPlaceholder() || ce.getValue() == null) { if (ce != null && ce.isChanged()) { ce.setValue(ice.getValue()); } else { if (isWrite) lockAndWrap(ctx, key, ice); else ctx.putLookedUpEntry(key, ice); } } } } else { if (trace) log.tracef("Not caching remotely retrieved entry for key %s in L1", key); } return ice.getValue(); } return null; }
@Override protected void commitContextEntry( CacheEntry entry, InvocationContext ctx, boolean skipOwnershipCheck) { if (ctx.isInTxScope()) { ClusteredRepeatableReadEntry clusterMvccEntry = (ClusteredRepeatableReadEntry) entry; EntryVersion existingVersion = clusterMvccEntry.getVersion(); EntryVersion newVersion; if (existingVersion == null) { newVersion = versionGenerator.generateNew(); } else { newVersion = versionGenerator.increment((IncrementableEntryVersion) existingVersion); } commitEntry(entry, newVersion, skipOwnershipCheck); } else { // This could be a state transfer call! commitEntry(entry, entry.getVersion(), skipOwnershipCheck); } }
private boolean commitEntryIfNeeded( final InvocationContext ctx, final FlagAffectedCommand command, final CacheEntry entry, final Flag stateTransferFlag, final Metadata metadata) { if (entry == null) { return false; } final boolean l1Invalidation = command instanceof InvalidateL1Command; if (entry.isChanged()) { if (trace) log.tracef("About to commit entry %s", entry); commitContextEntry(entry, ctx, command, metadata, stateTransferFlag, l1Invalidation); return true; } return false; }
public void transformForL1(CacheEntry entry) { if (entry.getLifespan() < 0 || entry.getLifespan() > configuration.getL1Lifespan()) entry.setLifespan(configuration.getL1Lifespan()); }
/** * Adds the listener using the provided filter converter and class loader. The provided builder is * used to add additional configuration including (clustered, onlyPrimary & identifier) which can * be used after this method is completed to see what values were used in the addition of this * listener * * @param listener * @param filter * @param converter * @param classLoader * @param <C> * @return */ public <C> void addListener( Object listener, CacheEventFilter<? super K, ? super V> filter, CacheEventConverter<? super K, ? super V, C> converter, ClassLoader classLoader) { Listener l = testListenerClassValidity(listener.getClass()); UUID generatedId = UUID.randomUUID(); CacheMode cacheMode = config.clustering().cacheMode(); CacheInvocationBuilder builder = new CacheInvocationBuilder(); builder .setIncludeCurrentState(l.includeCurrentState()) .setClustered(l.clustered()) .setOnlyPrimary( l.clustered() ? (cacheMode.isDistributed() ? true : false) : l.primaryOnly()) .setFilter(filter) .setConverter(converter) .setIdentifier(generatedId) .setClassLoader(classLoader); boolean foundMethods = validateAndAddListenerInvocation(listener, builder); if (foundMethods && l.clustered()) { if (cacheMode.isInvalidation()) { throw new UnsupportedOperationException( "Cluster listeners cannot be used with Invalidation Caches!"); } else if (cacheMode.isDistributed()) { clusterListenerIDs.put(listener, generatedId); EmbeddedCacheManager manager = cache.getCacheManager(); Address ourAddress = manager.getAddress(); List<Address> members = manager.getMembers(); // If we are the only member don't even worry about sending listeners if (members != null && members.size() > 1) { DistributedExecutionCompletionService decs = new DistributedExecutionCompletionService(distExecutorService); if (log.isTraceEnabled()) { log.tracef( "Replicating cluster listener to other nodes %s for cluster listener with id %s", members, generatedId); } Callable callable = new ClusterListenerReplicateCallable( generatedId, ourAddress, filter, converter, l.sync()); for (Address member : members) { if (!member.equals(ourAddress)) { decs.submit(member, callable); } } for (int i = 0; i < members.size() - 1; ++i) { try { decs.take().get(); } catch (InterruptedException e) { throw new CacheListenerException(e); } catch (ExecutionException e) { throw new CacheListenerException(e); } } int extraCount = 0; // If anyone else joined since we sent these we have to send the listeners again, since // they may have queried // before the other nodes got the new listener List<Address> membersAfter = manager.getMembers(); for (Address member : membersAfter) { if (!members.contains(member) && !member.equals(ourAddress)) { if (log.isTraceEnabled()) { log.tracef( "Found additional node %s that joined during replication of cluster listener with id %s", member, generatedId); } extraCount++; decs.submit(member, callable); } } for (int i = 0; i < extraCount; ++i) { try { decs.take().get(); } catch (InterruptedException e) { throw new CacheListenerException(e); } catch (ExecutionException e) { throw new CacheListenerException(e); } } } } } // If we have a segment listener handler, it means we have to do initial state QueueingSegmentListener handler = segmentHandler.remove(generatedId); if (handler != null) { if (log.isTraceEnabled()) { log.tracef("Listener %s requests initial state for cache", generatedId); } try (CloseableIterator<CacheEntry<K, C>> iterator = entryRetriever.retrieveEntries( filter == null ? null : new CacheEventFilterAsKeyValueFilter(filter), converter == null ? null : new CacheEventConverterAsConverter(converter), null, handler)) { while (iterator.hasNext()) { CacheEntry<K, C> entry = iterator.next(); // Mark the key as processed and see if we had a concurrent update Object value = handler.markKeyAsProcessing(entry.getKey()); if (value == BaseQueueingSegmentListener.REMOVED) { // Don't process this value if we had a concurrent remove continue; } raiseEventForInitialTransfer(generatedId, entry, builder.isClustered()); handler.notifiedKey(entry.getKey()); } } Set<CacheEntry> entries = handler.findCreatedEntries(); for (CacheEntry entry : entries) { raiseEventForInitialTransfer(generatedId, entry, builder.isClustered()); } if (log.isTraceEnabled()) { log.tracef("Listener %s initial state for cache completed", generatedId); } handler.transferComplete(); } }
protected void notifyCommitEntry( boolean created, boolean removed, boolean expired, CacheEntry entry, InvocationContext ctx, FlagAffectedCommand command, Object previousValue, Metadata previousMetadata) { boolean isWriteOnly = (command instanceof WriteCommand) && ((WriteCommand) command).isWriteOnly(); if (removed) { if (command instanceof RemoveCommand) { ((RemoveCommand) command).notify(ctx, previousValue, previousMetadata, false); } else { if (expired) { notifier.notifyCacheEntryExpired(entry.getKey(), previousValue, previousMetadata, ctx); } else { notifier.notifyCacheEntryRemoved( entry.getKey(), previousValue, previousMetadata, false, ctx, command); } // A write-only command only writes and so can't 100% guarantee // to be able to retrieve previous value when removed, so only // send remove event when the command is read-write. if (!isWriteOnly) functionalNotifier.notifyOnRemove( EntryViews.readOnly(entry.getKey(), previousValue, previousMetadata)); functionalNotifier.notifyOnWrite(() -> EntryViews.noValue(entry.getKey())); } } else { // Notify entry event after container has been updated if (created) { notifier.notifyCacheEntryCreated( entry.getKey(), entry.getValue(), entry.getMetadata(), false, ctx, command); // A write-only command only writes and so can't 100% guarantee // that an entry has been created, so only send create event // when the command is read-write. if (!isWriteOnly) functionalNotifier.notifyOnCreate(EntryViews.readOnly(entry)); functionalNotifier.notifyOnWrite(() -> EntryViews.readOnly(entry)); } else { notifier.notifyCacheEntryModified( entry.getKey(), entry.getValue(), entry.getMetadata(), previousValue, previousMetadata, false, ctx, command); // A write-only command only writes and so can't 100% guarantee // that an entry has been created, so only send modify when the // command is read-write. if (!isWriteOnly) functionalNotifier.notifyOnModify( EntryViews.readOnly(entry.getKey(), previousValue, previousMetadata), EntryViews.readOnly(entry)); functionalNotifier.notifyOnWrite(() -> EntryViews.readOnly(entry)); } } }
@Override protected void commitSingleEntry( CacheEntry entry, Metadata metadata, FlagAffectedCommand command, InvocationContext ctx, Flag trackFlag, boolean l1Invalidation) { // Don't allow the CH to change (and state transfer to invalidate entries) // between the ownership check and the commit stateTransferLock.acquireSharedTopologyLock(); try { boolean doCommit = true; // ignore locality for removals, even if skipOwnershipCheck is not true boolean skipOwnershipCheck = command != null && command.hasFlag(Flag.SKIP_OWNERSHIP_CHECK); boolean isForeignOwned = !skipOwnershipCheck && !localNodeIsOwner(entry.getKey()); if (isForeignOwned && !entry.isRemoved()) { if (configuration.clustering().l1().enabled()) { // transform for L1 long lifespan; if (metadata != null) { lifespan = metadata.lifespan(); } else { lifespan = entry.getLifespan(); } if (lifespan < 0 || lifespan > configuration.clustering().l1().lifespan()) { Metadata.Builder builder; if (metadata != null) { builder = metadata.builder(); } else { builder = entry.getMetadata().builder(); } metadata = builder.lifespan(configuration.clustering().l1().lifespan()).build(); } } else { doCommit = false; } } boolean created = false; boolean removed = false; boolean expired = false; if (!isForeignOwned) { created = entry.isCreated(); removed = entry.isRemoved(); if (removed && entry instanceof MVCCEntry) { expired = ((MVCCEntry) entry).isExpired(); } } if (doCommit) { InternalCacheEntry previousEntry = dataContainer.peek(entry.getKey()); Object previousValue = null; Metadata previousMetadata = null; if (previousEntry != null) { previousValue = previousEntry.getValue(); previousMetadata = previousEntry.getMetadata(); } commitManager.commit(entry, metadata, trackFlag, l1Invalidation); if (!isForeignOwned) { notifyCommitEntry( created, removed, expired, entry, ctx, command, previousValue, previousMetadata); } } else entry.rollback(); } finally { stateTransferLock.releaseSharedTopologyLock(); } }
@SuppressWarnings("UnusedParameters") protected EntryVersion getEntryVersion(InvocationContext ctx, Object key) { CacheEntry cacheEntry = dataContainer.get(key); return (cacheEntry != null) ? cacheEntry.getVersion() : null; }