/** * In case of a remotely originating transactions we don't have a chance to visit the single * commands but receive this "batch". We then need the before-apply snapshot of some types to * route the cleanup commands to the correct indexes. Note we don't need to visit the * CommitCommand as the indexing context is registered as a transaction sync. */ @Override public Object visitPrepareCommand(TxInvocationContext ctx, PrepareCommand command) throws Throwable { final WriteCommand[] writeCommands = command.getModifications(); final Object[] stateBeforePrepare = new Object[writeCommands.length]; for (int i = 0; i < writeCommands.length; i++) { final WriteCommand writeCommand = writeCommands[i]; if (writeCommand instanceof PutKeyValueCommand) { InternalCacheEntry internalCacheEntry = dataContainer.get(((PutKeyValueCommand) writeCommand).getKey()); stateBeforePrepare[i] = internalCacheEntry != null ? internalCacheEntry.getValue() : null; } else if (writeCommand instanceof PutMapCommand) { stateBeforePrepare[i] = getPreviousValues(((PutMapCommand) writeCommand).getMap().keySet()); } else if (writeCommand instanceof RemoveCommand) { InternalCacheEntry internalCacheEntry = dataContainer.get(((RemoveCommand) writeCommand).getKey()); stateBeforePrepare[i] = internalCacheEntry != null ? internalCacheEntry.getValue() : null; } else if (writeCommand instanceof ReplaceCommand) { InternalCacheEntry internalCacheEntry = dataContainer.get(((ReplaceCommand) writeCommand).getKey()); stateBeforePrepare[i] = internalCacheEntry != null ? internalCacheEntry.getValue() : null; } } final Object toReturn = super.visitPrepareCommand(ctx, command); if (ctx.isTransactionValid()) { final TransactionContext transactionContext = makeTransactionalEventContext(); for (int i = 0; i < writeCommands.length; i++) { final WriteCommand writeCommand = writeCommands[i]; if (writeCommand instanceof PutKeyValueCommand) { processPutKeyValueCommand( (PutKeyValueCommand) writeCommand, ctx, stateBeforePrepare[i], transactionContext); } else if (writeCommand instanceof PutMapCommand) { processPutMapCommand( (PutMapCommand) writeCommand, ctx, (Map<Object, Object>) stateBeforePrepare[i], transactionContext); } else if (writeCommand instanceof RemoveCommand) { processRemoveCommand( (RemoveCommand) writeCommand, ctx, stateBeforePrepare[i], transactionContext); } else if (writeCommand instanceof ReplaceCommand) { processReplaceCommand( (ReplaceCommand) writeCommand, ctx, stateBeforePrepare[i], transactionContext); } else if (writeCommand instanceof ClearCommand) { processClearCommand((ClearCommand) writeCommand, ctx, transactionContext); } } } return toReturn; }
@Override public void copyForUpdate(DataContainer container, boolean writeSkewCheck) { if (isChanged()) return; // already copied // mark entry as changed. setChanged(); if (writeSkewCheck) { // check for write skew. InternalCacheEntry ice = container.get(key); Object actualValue = ice == null ? null : ice.getValue(); // Note that this identity-check is intentional. We don't *want* to call actualValue.equals() // since that defeats the purpose. // the implicit "versioning" we have in R_R creates a new wrapper "value" instance for every // update. if (actualValue != null && actualValue != value) { String errormsg = new StringBuilder() .append("Detected write skew on key [") .append(getKey()) .append("]. Another process has changed the entry since we last read it!") .toString(); if (log.isWarnEnabled()) log.warn(errormsg + ". Unable to copy entry for update."); throw new CacheException(errormsg); } } // make a backup copy oldValue = value; }
private Map<Object, Object> getPreviousValues(Set<Object> keySet) { HashMap<Object, Object> previousValues = new HashMap<Object, Object>(); for (Object key : keySet) { InternalCacheEntry internalCacheEntry = dataContainer.get(key); Object previousValue = internalCacheEntry != null ? internalCacheEntry.getValue() : null; previousValues.put(key, previousValue); } return previousValues; }
public void testIdleExpiryInPut() throws InterruptedException { Cache<String, String> cache = cm.getCache(); long idleTime = IDLE_TIMEOUT; cache.put("k", "v", -1, MILLISECONDS, idleTime, MILLISECONDS); DataContainer dc = cache.getAdvancedCache().getDataContainer(); InternalCacheEntry se = dc.get("k", null); assert se.getKey().equals("k"); assert se.getValue().equals("v"); assert se.getLifespan() == -1; assert se.getMaxIdle() == idleTime; assert !se.isExpired(); assert cache.get("k").equals("v"); Thread.sleep(idleTime + 100); assert se.isExpired(); assert cache.get("k") == null; }
@SuppressWarnings("UnusedParameters") protected EntryVersion getEntryVersion(InvocationContext ctx, Object key) { CacheEntry cacheEntry = dataContainer.get(key); return (cacheEntry != null) ? cacheEntry.getVersion() : null; }