org.infinispan.container.EntryFactoryImpl Maven / Gradle / Ivy
package org.infinispan.container;
import static org.infinispan.commons.util.Util.toStr;
import org.infinispan.atomic.Delta;
import org.infinispan.atomic.DeltaAware;
import org.infinispan.configuration.cache.Configuration;
import org.infinispan.container.entries.CacheEntry;
import org.infinispan.container.entries.ClusteredRepeatableReadEntry;
import org.infinispan.container.entries.DeltaAwareCacheEntry;
import org.infinispan.container.entries.InternalCacheEntry;
import org.infinispan.container.entries.MVCCEntry;
import org.infinispan.container.entries.NullCacheEntry;
import org.infinispan.container.entries.ReadCommittedEntry;
import org.infinispan.container.entries.RepeatableReadEntry;
import org.infinispan.container.versioning.VersionGenerator;
import org.infinispan.context.InvocationContext;
import org.infinispan.factories.annotations.Inject;
import org.infinispan.factories.annotations.Start;
import org.infinispan.metadata.EmbeddedMetadata;
import org.infinispan.metadata.Metadata;
import org.infinispan.persistence.manager.PersistenceManager;
import org.infinispan.util.TimeService;
import org.infinispan.util.concurrent.IsolationLevel;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;
/**
* {@link EntryFactory} implementation to be used for optimistic locking scheme.
*
* @author Mircea Markus
* @since 5.1
*/
public class EntryFactoryImpl implements EntryFactory {
protected static final Log log = LogFactory.getLog(EntryFactoryImpl.class);
protected final static boolean trace = log.isTraceEnabled();
private boolean useRepeatableRead;
private DataContainer container;
private boolean isL1Enabled;
private Configuration configuration;
private PersistenceManager persistenceManager; // hack for DeltaAware
private TimeService timeService;
private VersionGenerator versionGenerator;
private boolean useVersioning;
@Inject
public void injectDependencies(DataContainer dataContainer, Configuration configuration,
TimeService timeService, PersistenceManager persistenceManager,
VersionGenerator versionGenerator) {
this.container = dataContainer;
this.configuration = configuration;
this.timeService = timeService;
this.persistenceManager = persistenceManager;
this.versionGenerator = versionGenerator;
}
@Start (priority = 8)
public void init() {
useRepeatableRead = configuration.transaction().transactionMode().isTransactional()
&& configuration.locking().isolationLevel() == IsolationLevel.REPEATABLE_READ;
isL1Enabled = configuration.clustering().l1().enabled();
// Write-skew check implies isolation level = REPEATABLE_READ && locking mode = OPTIMISTIC
useVersioning = configuration.clustering().cacheMode().isClustered() &&
configuration.transaction().transactionMode().isTransactional() &&
configuration.locking().writeSkewCheck();
}
@Override
public final void wrapEntryForReading(InvocationContext ctx, Object key, boolean isOwner) {
if (!isOwner && !isL1Enabled) {
return;
}
CacheEntry cacheEntry = getFromContext(ctx, key);
if (cacheEntry == null) {
cacheEntry = getFromContainer(key, isOwner, false);
if (cacheEntry != null) {
// With repeatable read, we need to create a RepeatableReadEntry as internal cache entries are mutable
// Otherwise we can store the InternalCacheEntry directly in the context
if (useRepeatableRead) {
cacheEntry = createWrappedEntry(key, cacheEntry);
}
ctx.putLookedUpEntry(key, cacheEntry);
}
}
if (trace) {
log.tracef("Wrap %s for read. Entry=%s", toStr(key), cacheEntry);
}
}
@Override
public void wrapEntryForWriting(InvocationContext ctx, Object key, boolean isOwner) {
CacheEntry contextEntry = getFromContext(ctx, key);
if (contextEntry instanceof MVCCEntry) {
// Nothing to do, already wrapped.
} else if (contextEntry != null) {
// Already in the context as an InternalCacheEntry or DeltaAwareCacheEntry.
// Need to wrap it in a MVCCEntry.
MVCCEntry mvccEntry = createWrappedEntry(key, contextEntry);
ctx.putLookedUpEntry(key, mvccEntry);
if (trace)
log.tracef("Updated context entry %s -> %s", contextEntry, mvccEntry);
} else {
// Not in the context yet.
CacheEntry cacheEntry = getFromContainer(key, isOwner, true);
if (cacheEntry == null) {
return;
}
MVCCEntry mvccEntry = createWrappedEntry(key, cacheEntry);
if (cacheEntry.isNull()) {
mvccEntry.setCreated(true);
}
ctx.putLookedUpEntry(key, mvccEntry);
if (trace)
log.tracef("Updated context entry %s -> %s", contextEntry, mvccEntry);
}
}
@Override
public void wrapExternalEntry(InvocationContext ctx, Object key, CacheEntry externalEntry, boolean isWrite) {
// For a write operation, the entry is always already wrapped. For a read operation, the entry may be
// in the context as an InternalCacheEntry, as null, or missing altogether.
CacheEntry contextEntry = getFromContext(ctx, key);
if (contextEntry instanceof MVCCEntry) {
// Already wrapped for a write. Update the value and the metadata.
if (contextEntry.skipLookup()) {
// This can happen during getGroup() invocations, which request the whole group from remote nodes
// even if some keys are already in the context.
if (trace)
log.tracef("Ignored update for context entry %s", contextEntry);
return;
}
contextEntry.setValue(externalEntry.getValue());
contextEntry.setCreated(externalEntry.getCreated());
contextEntry.setLastUsed(externalEntry.getLastUsed());
contextEntry.setMetadata(externalEntry.getMetadata());
((MVCCEntry) contextEntry).updatePreviousValue();
if (trace) log.tracef("Updated context entry %s", contextEntry);
} else if (contextEntry == null || contextEntry.isNull()) {
if (isWrite || useRepeatableRead) {
MVCCEntry mvccEntry = createWrappedEntry(key, externalEntry);
ctx.putLookedUpEntry(key, mvccEntry);
if (trace)
log.tracef("Updated context entry %s -> %s", contextEntry, mvccEntry);
} else {
// This is a read operation, store the external entry in the context directly.
ctx.putLookedUpEntry(key, externalEntry);
if (trace)
log.tracef("Updated context entry %s -> %s", contextEntry, externalEntry);
}
} else {
if (useRepeatableRead) {
if (trace) log.tracef("Ignored update %s -> %s as we do repeatable reads", contextEntry, externalEntry);
} else {
ctx.putLookedUpEntry(key, externalEntry);
if (trace) log.tracef("Updated context entry %s -> %s", contextEntry, externalEntry);
}
}
}
@Override
public void wrapEntryForDelta(InvocationContext ctx, Object deltaKey, Delta delta, boolean isOwner) {
CacheEntry cacheEntry = getFromContext(ctx, deltaKey);
DeltaAwareCacheEntry deltaAwareEntry;
if (cacheEntry instanceof DeltaAwareCacheEntry) {
// Already delta-aware, nothing to do.
deltaAwareEntry = (DeltaAwareCacheEntry) cacheEntry;
} else if (cacheEntry != null) {
// Wrap the existing context entry inside a DeltaAwareCacheEntry
deltaAwareEntry = createWrappedDeltaEntry(deltaKey, (DeltaAware) cacheEntry.getValue(), cacheEntry, ctx);
ctx.putLookedUpEntry(deltaKey, deltaAwareEntry);
} else {
// Read the value from the container and wrap it
cacheEntry = getFromContainer(deltaKey, isOwner, false);
DeltaAwareCacheEntry deltaEntry =
createWrappedDeltaEntry(deltaKey, cacheEntry != null ? (DeltaAware) cacheEntry.getValue() : null, null, ctx);
ctx.putLookedUpEntry(deltaKey, deltaEntry);
deltaAwareEntry = deltaEntry;
}
if (trace) log.tracef("Wrap %s for delta. Entry=%s", deltaKey, deltaAwareEntry);
}
private CacheEntry getFromContext(InvocationContext ctx, Object key) {
final CacheEntry cacheEntry = ctx.lookupEntry(key);
if (trace) log.tracef("Exists in context? %s ", cacheEntry);
return cacheEntry;
}
private CacheEntry getFromContainer(Object key, boolean isOwner, boolean writeOperation) {
if (isOwner) {
final InternalCacheEntry ice = innerGetFromContainer(key, writeOperation);
if (trace)
log.tracef("Retrieved from container %s", ice);
if (ice == null) {
return NullCacheEntry.getInstance();
}
return ice;
} else if (isL1Enabled) {
final InternalCacheEntry ice = innerGetFromContainer(key, writeOperation);
if (trace)
log.tracef("Retrieved from container %s", ice);
if (ice == null || !ice.isL1Entry()) return null;
return ice;
}
return null;
}
private InternalCacheEntry innerGetFromContainer(Object key, boolean writeOperation) {
InternalCacheEntry ice;
// Write operations should not cause expiration events to occur, because we will most likely overwrite the
// value anyways - also required for remove expired to not cause infinite loop
if (writeOperation) {
ice = container.peek(key);
if (ice != null && ice.canExpire()) {
long wallClockTime = timeService.wallClockTime();
if (ice.isExpired(wallClockTime)) {
ice = null;
} else {
ice.touch(wallClockTime);
}
}
} else {
ice = container.get(key);
}
return ice;
}
protected MVCCEntry createWrappedEntry(Object key, CacheEntry cacheEntry) {
Object value = null;
Metadata metadata = null;
if (cacheEntry != null) {
value = cacheEntry.getValue();
metadata = cacheEntry.getMetadata();
}
if (trace) log.tracef("Creating new entry for key %s", toStr(key));
if (useRepeatableRead) {
MVCCEntry mvccEntry;
if (useVersioning) {
if (metadata == null) {
metadata = new EmbeddedMetadata.Builder().version(versionGenerator.nonExistingVersion()).build();
}
mvccEntry = new ClusteredRepeatableReadEntry(key, value, metadata);
} else {
mvccEntry = new RepeatableReadEntry(key, value, metadata);
}
return mvccEntry;
} else {
return new ReadCommittedEntry(key, value, metadata);
}
}
private DeltaAwareCacheEntry createWrappedDeltaEntry(Object key, DeltaAware deltaAware, CacheEntry entry, InvocationContext ctx) {
DeltaAwareCacheEntry deltaAwareCacheEntry = new DeltaAwareCacheEntry(key, deltaAware, entry, ctx, persistenceManager, timeService);
// Set the delta aware entry to created so it ignores the previous value and only merges new deltas when it is
// committed
if (entry != null && entry.isCreated()) {
deltaAwareCacheEntry.setCreated(true);
}
return deltaAwareCacheEntry;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy