private CompletableFuture<Void> visitSecondPhaseCommand(
      TxInvocationContext ctx,
      TransactionBoundaryCommand command,
      boolean commit,
      ExtendedStatistic duration,
      ExtendedStatistic counter)
      throws Throwable {
    GlobalTransaction globalTransaction = command.getGlobalTransaction();
    if (trace) {
      log.tracef(
          "Visit 2nd phase command %s. Is it local? %s. Transaction is %s",
          command, ctx.isOriginLocal(), globalTransaction.globalId());
    }
    long start = timeService.time();
    return ctx.onReturn(
        (rCtx, rCommand, rv, throwable) -> {
          if (throwable != null) {
            throw throwable;
          }

          long end = timeService.time();
          updateTime(duration, counter, start, end, globalTransaction, rCtx.isOriginLocal());
          cacheStatisticManager.setTransactionOutcome(
              commit, globalTransaction, rCtx.isOriginLocal());
          cacheStatisticManager.terminateTransaction(globalTransaction, true, true);
          return null;
        });
  }
  @Override
  public CompletableFuture<Void> visitPrepareCommand(
      TxInvocationContext ctx, PrepareCommand command) throws Throwable {
    GlobalTransaction globalTransaction = command.getGlobalTransaction();
    if (trace) {
      log.tracef(
          "Visit Prepare command %s. Is it local?. Transaction is %s",
          command, ctx.isOriginLocal(), globalTransaction.globalId());
    }
    initStatsIfNecessary(ctx);
    cacheStatisticManager.onPrepareCommand(globalTransaction, ctx.isOriginLocal());
    if (command.hasModifications()) {
      cacheStatisticManager.markAsWriteTransaction(globalTransaction, ctx.isOriginLocal());
    }

    long start = timeService.time();
    return ctx.onReturn(
        (rCtx, rCommand, rv, throwable) -> {
          if (throwable != null) {
            processWriteException(rCtx, globalTransaction, throwable);
          } else {
            long end = timeService.time();
            updateTime(
                PREPARE_EXECUTION_TIME,
                NUM_PREPARE_COMMAND,
                start,
                end,
                globalTransaction,
                rCtx.isOriginLocal());
          }

          if (((PrepareCommand) rCommand).isOnePhaseCommit()) {
            boolean local = rCtx.isOriginLocal();
            boolean success = throwable == null;
            cacheStatisticManager.setTransactionOutcome(
                success, globalTransaction, rCtx.isOriginLocal());
            cacheStatisticManager.terminateTransaction(globalTransaction, local, !local);
          }
          return null;
        });
  }