@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);
        }
      }
    }
  }
 /**
  * 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);
 }
 /**
  * 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);
 }
 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);
       }
     }
   }
 }
 @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);
         }
       }
     }
   }
 }