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());
 }
Пример #2
0
 /**
  * Simply check if there is an ongoing tx.
  *
  * <ul>
  *   <li>If there is one, this is a no-op and just passes the call up the chain.
  *   <li>If there isn't one and there is a batch in progress, resume the batch's tx, pass up, and
  *       finally suspend the batch's tx.
  *   <li>If there is no batch in progress, just pass the call up the chain.
  * </ul>
  */
 @Override
 protected Object handleDefault(InvocationContext ctx, VisitableCommand command) throws Throwable {
   Transaction tx;
   if (!ctx.isOriginLocal()) return invokeNextInterceptor(ctx, command);
   // if in a batch, attach tx
   if (transactionManager.getTransaction() == null
       && (tx = batchContainer.getBatchTransaction()) != null) {
     try {
       transactionManager.resume(tx);
       // If there's no ongoing tx then BatchingInterceptor creates one and then invokes next
       // interceptor,
       // so that all interceptors in the stack will be executed in a transactional context.
       // This is where a new context (TxInvocationContext) is created, as the existing context is
       // not transactional: NonTxInvocationContext.
       InvocationContext txContext = icc.createInvocationContext(true, -1);
       txContext.setFlags(ctx.getFlags());
       return invokeNextInterceptor(txContext, command);
     } finally {
       if (transactionManager.getTransaction() != null
           && batchContainer.isSuspendTxAfterInvocation()) transactionManager.suspend();
     }
   } else {
     return invokeNextInterceptor(ctx, command);
   }
 }
Пример #3
0
  @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);
        }
      }
    }
  }
Пример #5
0
  private InvocationContext setInvocationContextFlagsAndClassLoader(
      InvocationContext ctx, EnumSet<Flag> explicitFlags, ClassLoader explicitClassLoader) {
    if (explicitFlags != null) ctx.setFlags(explicitFlags);
    if (explicitClassLoader != null) ctx.setClassLoader(explicitClassLoader);

    return ctx;
  }
Пример #6
0
  @Override
  public Object perform(InvocationContext ctx) throws Throwable {
    MVCCEntry e = (MVCCEntry) ctx.lookupEntry(key);
    if (e != null) {
      if (ctx.isOriginLocal()) {
        // ISPN-514
        if (e.isNull() || e.getValue() == null) {
          // Revert assumption that new value is to be committed
          e.setChanged(false);
          return returnValue(null, false, ctx);
        }
      }

      if (oldValue == null || oldValue.equals(e.getValue()) || ignorePreviousValue) {
        e.setChanged(true);
        Object old = e.setValue(newValue);
        e.setLifespan(lifespanMillis);
        e.setMaxIdle(maxIdleTimeMillis);
        e.setLoaded(false);
        return returnValue(old, true, ctx);
      }
      // Revert assumption that new value is to be committed
      e.setChanged(false);
    }

    return returnValue(null, false, ctx);
  }
Пример #7
0
 @SuppressWarnings("unchecked")
 final V get(Object key, EnumSet<Flag> explicitFlags, ClassLoader explicitClassLoader) {
   assertKeyNotNull(key);
   InvocationContext ctx = getInvocationContextForRead(null, explicitFlags, explicitClassLoader);
   GetKeyValueCommand command = commandsFactory.buildGetKeyValueCommand(key, ctx.getFlags());
   return (V) invoker.invoke(ctx, command);
 }
 private Future<?> flushL1Caches(InvocationContext ctx) {
   // TODO how do we tell the L1 manager which keys are removed and which keys may still exist in
   // remote L1?
   return isL1CacheEnabled
       ? l1Manager.flushCacheWithSimpleFuture(ctx.getLockedKeys(), null, ctx.getOrigin(), true)
       : null;
 }
  private Object handleTopologyAffectedCommand(
      InvocationContext ctx, VisitableCommand command, Address origin, boolean sync)
      throws Throwable {
    log.tracef("handleTopologyAffectedCommand for command %s", command);

    if (isLocalOnly(ctx, command)) {
      return invokeNextInterceptor(ctx, command);
    }
    updateTopologyId((TopologyAffectedCommand) command);

    // TODO we may need to skip local invocation for read/write/tx commands if the command is too
    // old and none of its keys are local
    Object localResult = invokeNextInterceptor(ctx, command);

    boolean isNonTransactionalWrite = !ctx.isInTxScope() && command instanceof WriteCommand;
    boolean isTransactionalAndNotRolledBack = false;
    if (ctx.isInTxScope()) {
      isTransactionalAndNotRolledBack =
          !((TxInvocationContext) ctx).getCacheTransaction().isMarkedForRollback();
    }

    if (isNonTransactionalWrite || isTransactionalAndNotRolledBack) {
      stateTransferManager.forwardCommandIfNeeded(
          ((TopologyAffectedCommand) command), getAffectedKeys(ctx, command), origin, sync);
    }

    return localResult;
  }
 @Override
 public CompletableFuture<Void> visitGetKeysInGroupCommand(
     final InvocationContext ctx, GetKeysInGroupCommand command) throws Throwable {
   final String groupName = command.getGroupName();
   if (!command.isGroupOwner()) {
     return ctx.continueInvocation();
   }
   final KeyFilter<Object> keyFilter =
       new CompositeKeyFilter<>(
           new GroupFilter<>(groupName, groupManager),
           new CollectionKeyFilter<>(ctx.getLookedUpEntries().keySet()));
   dataContainer.executeTask(
       keyFilter,
       (o, internalCacheEntry) -> {
         synchronized (ctx) {
           // the process can be made in multiple threads, so we need to synchronize in the
           // context.
           entryFactory.wrapExternalEntry(
               ctx,
               internalCacheEntry.getKey(),
               internalCacheEntry,
               EntryFactory.Wrap.STORE,
               false);
         }
       });
   return ctx.continueInvocation();
 }
 @Override
 protected void checkIfKeyRead(InvocationContext context, Object key, VisitableCommand command) {
   if (command instanceof AbstractDataWriteCommand) {
     AbstractDataWriteCommand writeCommand = (AbstractDataWriteCommand) command;
     // keep track is only need in a clustered and transactional environment to perform the write
     // skew check
     if (context.isInTxScope() && context.isOriginLocal()) {
       TxInvocationContext txInvocationContext = (TxInvocationContext) context;
       if (!writeCommand.hasFlag(Flag.PUT_FOR_STATE_TRANSFER) && writeCommand.isConditional()
           || !writeCommand.hasFlag(Flag.IGNORE_RETURN_VALUES)) {
         // State transfer does not show the old value for the application neither with the
         // IGNORE_RETURN_VALUES.
         // on other hand, the conditional always read key!
         txInvocationContext.getCacheTransaction().addReadKey(key);
       }
       writeCommand.setPreviousRead(txInvocationContext.getCacheTransaction().keyRead(key));
     }
   } else if (command instanceof GetKeyValueCommand) {
     if (context.isInTxScope() && context.isOriginLocal()) {
       // always show the value to the application
       TxInvocationContext txInvocationContext = (TxInvocationContext) context;
       txInvocationContext.getCacheTransaction().addReadKey(key);
     }
   }
 }
  @Override
  public Object visitPutMapCommand(InvocationContext ctx, PutMapCommand command) throws Throwable {
    Object returnValue = invokeNextInterceptor(ctx, command);
    if (!isStoreEnabled(command) || ctx.isInTxScope()) return returnValue;

    Map<Object, Object> map = command.getMap();
    int count = 0;
    for (Object key : map.keySet()) {
      // In non-tx mode, a node may receive the same forwarded PutMapCommand many times - but each
      // time
      // it must write only the keys locked on the primary owner that forwarded the command
      if (isUsingLockDelegation
          && command.isForwarded()
          && !dm.getPrimaryLocation(key).equals(ctx.getOrigin())) continue;

      if (isProperWriter(ctx, command, key)) {
        InternalCacheEntry se = getStoredEntry(key, ctx);
        store.store(se);
        log.tracef("Stored entry %s under key %s", se, key);
        count++;
      }
    }
    if (getStatisticsEnabled()) cacheStores.getAndAdd(count);
    return returnValue;
  }
Пример #13
0
  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);
  }
Пример #14
0
 @SuppressWarnings("unchecked")
 final V remove(Object key, EnumSet<Flag> explicitFlags, ClassLoader explicitClassLoader) {
   assertKeyNotNull(key);
   InvocationContext ctx =
       getInvocationContextWithImplicitTransaction(explicitFlags, explicitClassLoader);
   RemoveCommand command = commandsFactory.buildRemoveCommand(key, null, ctx.getFlags());
   return (V) executeCommandAndCommitIfNeeded(ctx, command);
 }
Пример #15
0
 final boolean containsKey(
     Object key, EnumSet<Flag> explicitFlags, ClassLoader explicitClassLoader) {
   assertKeyNotNull(key);
   InvocationContext ctx = getInvocationContextForRead(null, explicitFlags, explicitClassLoader);
   GetKeyValueCommand command = commandsFactory.buildGetKeyValueCommand(key, ctx.getFlags());
   Object response = invoker.invoke(ctx, command);
   return response != null;
 }
Пример #16
0
 final boolean remove(
     Object key, Object value, EnumSet<Flag> explicitFlags, ClassLoader explicitClassLoader) {
   assertKeyNotNull(key);
   InvocationContext ctx =
       getInvocationContextWithImplicitTransaction(explicitFlags, explicitClassLoader);
   RemoveCommand command = commandsFactory.buildRemoveCommand(key, value, ctx.getFlags());
   return (Boolean) executeCommandAndCommitIfNeeded(ctx, command);
 }
Пример #17
0
 final NotifyingFuture<Void> clearAsync(
     EnumSet<Flag> explicitFlags, ClassLoader explicitClassLoader) {
   InvocationContext ctx =
       getInvocationContextWithImplicitTransaction(explicitFlags, explicitClassLoader);
   ctx.setUseFutureReturnType(true);
   ClearCommand command = commandsFactory.buildClearCommand(ctx.getFlags());
   return wrapInFuture(executeCommandAndCommitIfNeeded(ctx, command));
 }
 private boolean isClusterInvocation(InvocationContext ctx) {
   // If the cache is local, the interceptor should only be enabled in case
   // of lazy deserialization or when an async store is in place. So, if
   // any cache store is configured, check whether it'll be skipped
   return ctx.isOriginLocal()
       && configuration.getCacheMode().isClustered()
       && !ctx.hasFlag(Flag.CACHE_MODE_LOCAL);
 }
  @Override
  public Object visitClearCommand(InvocationContext ctx, ClearCommand command) throws Throwable {
    // Clear is not key specific, so take into account origin of call
    if ((ctx.isOriginLocal() || !loaderConfig.shared())
        && !skip(ctx, command)
        && !ctx.isInTxScope()) clearCacheStore();

    return invokeNextInterceptor(ctx, command);
  }
Пример #20
0
 final NotifyingFuture<Boolean> removeAsync(
     Object key, Object value, EnumSet<Flag> explicitFlags, ClassLoader explicitClassLoader) {
   assertKeyNotNull(key);
   InvocationContext ctx =
       getInvocationContextWithImplicitTransaction(explicitFlags, explicitClassLoader);
   ctx.setUseFutureReturnType(true);
   RemoveCommand command = commandsFactory.buildRemoveCommand(key, value, ctx.getFlags());
   return wrapInFuture(executeCommandAndCommitIfNeeded(ctx, command));
 }
 private Object markTxForRollbackAndRethrow(InvocationContext ctx, Throwable te) throws Throwable {
   if (ctx.isOriginLocal() && ctx.isInTxScope()) {
     Transaction transaction = tm.getTransaction();
     if (transaction != null && isValidRunningTx(transaction)) {
       transaction.setRollbackOnly();
     }
   }
   throw te;
 }
  private Object invokeNextAndApplyChanges(
      InvocationContext ctx, FlagAffectedCommand command, Metadata metadata) throws Throwable {
    final Object result = ctx.forkInvocationSync(command);

    if (!ctx.isInTxScope()) {
      stateTransferLock.acquireSharedTopologyLock();
      try {
        // We only retry non-tx write commands
        if (command instanceof WriteCommand) {
          WriteCommand writeCommand = (WriteCommand) command;
          // Can't perform the check during preload or if the cache isn't clustered
          boolean isSync =
              (cacheConfiguration.clustering().cacheMode().isSynchronous()
                      && !command.hasFlag(Flag.FORCE_ASYNCHRONOUS))
                  || command.hasFlag(Flag.FORCE_SYNCHRONOUS);
          if (writeCommand.isSuccessful()
              && stateConsumer != null
              && stateConsumer.getCacheTopology() != null) {
            int commandTopologyId = command.getTopologyId();
            int currentTopologyId = stateConsumer.getCacheTopology().getTopologyId();
            // TotalOrderStateTransferInterceptor doesn't set the topology id for PFERs.
            if (isSync && currentTopologyId != commandTopologyId && commandTopologyId != -1) {
              // If we were the originator of a data command which we didn't own the key at the time
              // means it
              // was already committed, so there is no need to throw the OutdatedTopologyException
              // This will happen if we submit a command to the primary owner and it responds and
              // then a topology
              // change happens before we get here
              if (!ctx.isOriginLocal()
                  || !(command instanceof DataCommand)
                  || ctx.hasLockedKey(((DataCommand) command).getKey())) {
                if (trace)
                  log.tracef(
                      "Cache topology changed while the command was executing: expected %d, got %d",
                      commandTopologyId, currentTopologyId);
                // This shouldn't be necessary, as we'll have a fresh command instance when retrying
                writeCommand.setValueMatcher(writeCommand.getValueMatcher().matcherForRetry());
                throw new OutdatedTopologyException(
                    "Cache topology changed while the command was executing: expected "
                        + commandTopologyId
                        + ", got "
                        + currentTopologyId);
              }
            }
          }
        }

        commitContextEntries(ctx, command, metadata);
      } finally {
        stateTransferLock.releaseSharedTopologyLock();
      }
    }

    if (trace) log.tracef("The return value is %s", toStr(result));
    return result;
  }
Пример #23
0
 boolean lock(
     Collection<? extends K> keys, EnumSet<Flag> explicitFlags, ClassLoader explicitClassLoader) {
   if (keys == null || keys.isEmpty()) {
     throw new IllegalArgumentException("Cannot lock empty list of keys");
   }
   InvocationContext ctx = getInvocationContextForWrite(explicitFlags, explicitClassLoader);
   LockControlCommand command =
       commandsFactory.buildLockControlCommand(keys, false, ctx.getFlags());
   return (Boolean) invoker.invoke(ctx, command);
 }
  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;
  }
Пример #25
0
 @Override
 protected ClassLoader getClassLoader() {
   if (icc != null) {
     InvocationContext ctx = icc.getInvocationContext(true);
     if (ctx != null) {
       ClassLoader cl = ctx.getClassLoader();
       if (cl != null) return cl;
     }
   }
   return super.getClassLoader();
 }
 private Object handleInvalidate(InvocationContext ctx, WriteCommand command, Object... keys)
     throws Throwable {
   Object retval = invokeNextInterceptor(ctx, command);
   if (command.isSuccessful() && !ctx.isInTxScope()) {
     if (keys != null && keys.length != 0) {
       return invalidateAcrossCluster(
           isSynchronous(ctx), ctx, keys, ctx.isUseFutureReturnType(), retval);
     }
   }
   return retval;
 }
 /**
  * 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 void updateTransactionVersion(InvocationContext context) {
    if (!context.isInTxScope() && !context.isOriginLocal()) {
      return;
    }

    if (context instanceof SingleKeyNonTxInvocationContext) {
      if (log.isDebugEnabled()) {
        log.debugf(
            "Received a SingleKeyNonTxInvocationContext... This should be a single read operation");
      }
      return;
    }

    TxInvocationContext txInvocationContext = (TxInvocationContext) context;
    List<EntryVersion> entryVersionList = new LinkedList<EntryVersion>();
    entryVersionList.add(txInvocationContext.getTransactionVersion());

    if (log.isTraceEnabled()) {
      log.tracef(
          "[%s] Keys read in this command: %s",
          txInvocationContext.getGlobalTransaction().prettyPrint(),
          txInvocationContext.getKeysReadInCommand());
    }

    for (InternalGMUCacheEntry internalGMUCacheEntry :
        txInvocationContext.getKeysReadInCommand().values()) {
      Object key = internalGMUCacheEntry.getKey();
      boolean local = cll.localNodeIsOwner(key);
      if (log.isTraceEnabled()) {
        log.tracef(
            "[%s] Analyze entry [%s]: local?=%s",
            txInvocationContext.getGlobalTransaction().prettyPrint(), internalGMUCacheEntry, local);
      }
      if (txInvocationContext.hasModifications() && !internalGMUCacheEntry.isMostRecent()) {
        throw new CacheException("Read-Write transaction read an old value and should rollback");
      }

      if (internalGMUCacheEntry.getMaximumTransactionVersion() != null) {
        entryVersionList.add(internalGMUCacheEntry.getMaximumTransactionVersion());
      }
      txInvocationContext.getCacheTransaction().addReadKey(key);
      if (local) {
        txInvocationContext.setAlreadyReadOnThisNode(true);
        txInvocationContext.addReadFrom(cll.getAddress());
      }
    }

    if (entryVersionList.size() > 1) {
      EntryVersion[] txVersionArray = new EntryVersion[entryVersionList.size()];
      txInvocationContext.setTransactionVersion(
          versionGenerator.mergeAndMax(entryVersionList.toArray(txVersionArray)));
    }
  }
 /**
  * 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 applyRemoteTxLog(List<WriteCommand> commands) {
   for (WriteCommand cmd : commands) {
     try {
       // this is a remotely originating tx
       cf.initializeReplicableCommand(cmd, true);
       InvocationContext ctx = icc.createInvocationContext();
       ctx.setFlags(SKIP_REMOTE_LOOKUP, CACHE_MODE_LOCAL, SKIP_SHARED_CACHE_STORE, SKIP_LOCKING);
       interceptorChain.invoke(ctx, cmd);
     } catch (Exception e) {
       log.exceptionWhenReplaying(cmd, e);
     }
   }
 }