private <R> String formatNodeValuesFromGetVersions(
      List<Response<ByteArray, List<Version>>> results) {
    // log all retrieved values
    StringBuilder builder = new StringBuilder();
    builder.append("{");
    for (Response<ByteArray, List<Version>> r : results) {
      builder.append(
          "(nodeId="
              + r.getNode().getId()
              + ", key="
              + ByteUtils.toHexString(r.getKey().get())
              + ", retrieved= "
              + r.getValue()
              + "), ");
    }
    builder.append("}");

    return builder.toString();
  }
  public void put(
      ByteArray key, Versioned<byte[]> versioned, byte[] transforms, long putOpTimeoutInMs)
      throws VoldemortException {

    long startTimeMs = -1;
    long startTimeNs = -1;

    if (logger.isDebugEnabled()) {
      startTimeMs = System.currentTimeMillis();
      startTimeNs = System.nanoTime();
    }

    StoreUtils.assertValidKey(key);
    PutPipelineData pipelineData = new PutPipelineData();
    if (zoneRoutingEnabled) pipelineData.setZonesRequired(storeDef.getZoneCountWrites());
    else pipelineData.setZonesRequired(null);
    pipelineData.setStartTimeNs(System.nanoTime());
    pipelineData.setStoreName(getName());
    pipelineData.setStats(stats);

    Pipeline pipeline = new Pipeline(Operation.PUT, putOpTimeoutInMs, TimeUnit.MILLISECONDS);
    pipeline.setEnableHintedHandoff(isHintedHandoffEnabled());

    HintedHandoff hintedHandoff = null;

    // Get the correct type of configure nodes action depending on the store
    // requirements
    AbstractConfigureNodes<ByteArray, Void, PutPipelineData> configureNodes =
        makeNodeConfigurationForPut(pipelineData, key);

    if (isHintedHandoffEnabled())
      hintedHandoff =
          new HintedHandoff(
              failureDetector,
              slopStores,
              nonblockingSlopStores,
              handoffStrategy,
              pipelineData.getFailedNodes(),
              putOpTimeoutInMs);

    pipeline.addEventAction(Event.STARTED, configureNodes);

    pipeline.addEventAction(
        Event.CONFIGURED,
        new PerformSerialPutRequests(
            pipelineData,
            isHintedHandoffEnabled() ? Event.RESPONSES_RECEIVED : Event.COMPLETED,
            key,
            transforms,
            failureDetector,
            innerStores,
            storeDef.getRequiredWrites(),
            versioned,
            time,
            Event.MASTER_DETERMINED));
    pipeline.addEventAction(
        Event.MASTER_DETERMINED,
        new PerformParallelPutRequests(
            pipelineData,
            Event.RESPONSES_RECEIVED,
            key,
            transforms,
            failureDetector,
            storeDef.getPreferredWrites(),
            storeDef.getRequiredWrites(),
            putOpTimeoutInMs,
            nonblockingStores,
            hintedHandoff));
    if (isHintedHandoffEnabled()) {
      pipeline.addEventAction(
          Event.ABORTED,
          new PerformPutHintedHandoff(
              pipelineData, Event.ERROR, key, versioned, transforms, hintedHandoff, time));
      pipeline.addEventAction(
          Event.RESPONSES_RECEIVED,
          new PerformPutHintedHandoff(
              pipelineData,
              Event.HANDOFF_FINISHED,
              key,
              versioned,
              transforms,
              hintedHandoff,
              time));
      pipeline.addEventAction(
          Event.HANDOFF_FINISHED,
          new IncrementClock(pipelineData, Event.COMPLETED, versioned, time));
    } else
      pipeline.addEventAction(
          Event.RESPONSES_RECEIVED,
          new IncrementClock(pipelineData, Event.COMPLETED, versioned, time));

    pipeline.addEvent(Event.STARTED);
    if (logger.isDebugEnabled()) {
      logger.debug(
          "Operation "
              + pipeline.getOperation().getSimpleName()
              + " Key "
              + ByteUtils.toHexString(key.get()));
    }
    try {
      pipeline.execute();
    } catch (VoldemortException e) {
      stats.reportException(e);
      throw e;
    }

    if (logger.isDebugEnabled()) {
      logger.debug(
          "Finished "
              + pipeline.getOperation().getSimpleName()
              + " for key "
              + ByteUtils.toHexString(key.get())
              + " keyRef: "
              + System.identityHashCode(key)
              + "; started at "
              + startTimeMs
              + " took "
              + (System.nanoTime() - startTimeNs)
              + " value: "
              + versioned.getValue()
              + " (size: "
              + versioned.getValue().length
              + ")");
    }

    if (pipelineData.getFatalError() != null) throw pipelineData.getFatalError();
  }
  protected boolean delete(final ByteArray key, final Version version, long deleteOpTimeout)
      throws VoldemortException {
    StoreUtils.assertValidKey(key);

    long startTimeMs = -1;
    long startTimeNs = -1;

    if (logger.isDebugEnabled()) {
      startTimeMs = System.currentTimeMillis();
      startTimeNs = System.nanoTime();
    }

    BasicPipelineData<Boolean> pipelineData = new BasicPipelineData<Boolean>();
    if (zoneRoutingEnabled) pipelineData.setZonesRequired(storeDef.getZoneCountWrites());
    else pipelineData.setZonesRequired(null);
    pipelineData.setStoreName(getName());
    pipelineData.setStats(stats);

    Pipeline pipeline = new Pipeline(Operation.DELETE, deleteOpTimeout, TimeUnit.MILLISECONDS);
    pipeline.setEnableHintedHandoff(isHintedHandoffEnabled());

    HintedHandoff hintedHandoff = null;

    if (isHintedHandoffEnabled())
      hintedHandoff =
          new HintedHandoff(
              failureDetector,
              slopStores,
              nonblockingSlopStores,
              handoffStrategy,
              pipelineData.getFailedNodes(),
              deleteOpTimeout);

    pipeline.addEventAction(
        Event.STARTED,
        new ConfigureNodes<Boolean, BasicPipelineData<Boolean>>(
            pipelineData,
            Event.CONFIGURED,
            failureDetector,
            storeDef.getRequiredWrites(),
            routingStrategy,
            key,
            clientZone));
    pipeline.addEventAction(
        Event.CONFIGURED,
        new PerformParallelDeleteRequests<Boolean, BasicPipelineData<Boolean>>(
            pipelineData,
            isHintedHandoffEnabled() ? Event.RESPONSES_RECEIVED : Event.COMPLETED,
            key,
            failureDetector,
            storeDef.getPreferredWrites(),
            storeDef.getRequiredWrites(),
            deleteOpTimeout,
            nonblockingStores,
            hintedHandoff,
            version));

    if (isHintedHandoffEnabled()) {
      pipeline.addEventAction(
          Event.RESPONSES_RECEIVED,
          new PerformDeleteHintedHandoff(
              pipelineData, Event.COMPLETED, key, version, hintedHandoff));
      pipeline.addEventAction(
          Event.ABORTED,
          new PerformDeleteHintedHandoff(pipelineData, Event.ERROR, key, version, hintedHandoff));
    }

    pipeline.addEvent(Event.STARTED);
    if (logger.isDebugEnabled()) {
      logger.debug(
          "Operation "
              + pipeline.getOperation().getSimpleName()
              + " Key "
              + ByteUtils.toHexString(key.get()));
    }
    try {
      pipeline.execute();
    } catch (VoldemortException e) {
      stats.reportException(e);
      throw e;
    }

    if (logger.isDebugEnabled()) {
      logger.debug(
          "Finished "
              + pipeline.getOperation().getSimpleName()
              + " for key "
              + ByteUtils.toHexString(key.get())
              + " keyRef: "
              + System.identityHashCode(key)
              + "; started at "
              + startTimeMs
              + " took "
              + (System.nanoTime() - startTimeNs));
    }

    if (pipelineData.getFatalError() != null) throw pipelineData.getFatalError();

    for (Response<ByteArray, Boolean> response : pipelineData.getResponses()) {
      if (response.getValue().booleanValue()) return true;
    }

    return false;
  }
  @Override
  public List<Version> getVersions(final ByteArray key) {
    StoreUtils.assertValidKey(key);

    long startTimeMs = -1;
    long startTimeNs = -1;

    if (logger.isDebugEnabled()) {
      startTimeMs = System.currentTimeMillis();
      startTimeNs = System.nanoTime();
    }

    BasicPipelineData<List<Version>> pipelineData = new BasicPipelineData<List<Version>>();
    if (zoneRoutingEnabled) pipelineData.setZonesRequired(storeDef.getZoneCountReads());
    else pipelineData.setZonesRequired(null);
    pipelineData.setStats(stats);
    Pipeline pipeline =
        new Pipeline(
            Operation.GET_VERSIONS,
            timeoutConfig.getOperationTimeout(VoldemortOpCode.GET_VERSION_OP_CODE),
            TimeUnit.MILLISECONDS);

    StoreRequest<List<Version>> blockingStoreRequest =
        new StoreRequest<List<Version>>() {

          @Override
          public List<Version> request(Store<ByteArray, byte[], byte[]> store) {
            return store.getVersions(key);
          }
        };

    if (zoneAffinity.isGetVersionsOpZoneAffinityEnabled()) {
      pipeline.addEventAction(
          Event.STARTED,
          new ConfigureNodesLocalZoneOnly<List<Version>, BasicPipelineData<List<Version>>>(
              pipelineData,
              Event.CONFIGURED,
              failureDetector,
              storeDef.getRequiredReads(),
              routingStrategy,
              key,
              clientZone));
    } else {
      pipeline.addEventAction(
          Event.STARTED,
          new ConfigureNodes<List<Version>, BasicPipelineData<List<Version>>>(
              pipelineData,
              Event.CONFIGURED,
              failureDetector,
              storeDef.getRequiredReads(),
              routingStrategy,
              key,
              clientZone));
    }
    pipeline.addEventAction(
        Event.CONFIGURED,
        new PerformParallelRequests<List<Version>, BasicPipelineData<List<Version>>>(
            pipelineData,
            Event.COMPLETED,
            key,
            null,
            failureDetector,
            storeDef.getPreferredReads(),
            storeDef.getRequiredReads(),
            timeoutConfig.getOperationTimeout(VoldemortOpCode.GET_VERSION_OP_CODE),
            nonblockingStores,
            Event.INSUFFICIENT_SUCCESSES,
            Event.INSUFFICIENT_ZONES));

    pipeline.addEventAction(
        Event.INSUFFICIENT_SUCCESSES,
        new PerformSerialRequests<List<Version>, BasicPipelineData<List<Version>>>(
            pipelineData,
            Event.COMPLETED,
            key,
            failureDetector,
            innerStores,
            storeDef.getPreferredReads(),
            storeDef.getRequiredReads(),
            blockingStoreRequest,
            null));

    if (zoneRoutingEnabled)
      pipeline.addEventAction(
          Event.INSUFFICIENT_ZONES,
          new PerformZoneSerialRequests<List<Version>, BasicPipelineData<List<Version>>>(
              pipelineData,
              Event.COMPLETED,
              key,
              failureDetector,
              innerStores,
              blockingStoreRequest));

    pipeline.addEvent(Event.STARTED);
    if (logger.isDebugEnabled()) {
      logger.debug(
          "Operation  "
              + pipeline.getOperation().getSimpleName()
              + " Key "
              + ByteUtils.toHexString(key.get()));
    }
    try {
      pipeline.execute();
    } catch (VoldemortException e) {
      stats.reportException(e);
      throw e;
    }

    if (pipelineData.getFatalError() != null) throw pipelineData.getFatalError();

    List<Version> results = new ArrayList<Version>();

    for (Response<ByteArray, List<Version>> response : pipelineData.getResponses())
      results.addAll(response.getValue());

    if (logger.isDebugEnabled()) {
      logger.debug(
          "Finished "
              + pipeline.getOperation().getSimpleName()
              + " for key "
              + ByteUtils.toHexString(key.get())
              + " keyRef: "
              + System.identityHashCode(key)
              + "; started at "
              + startTimeMs
              + " took "
              + (System.nanoTime() - startTimeNs)
              + " values: "
              + formatNodeValuesFromGetVersions(pipelineData.getResponses()));
    }

    return results;
  }
  public Map<ByteArray, List<Versioned<byte[]>>> getAll(
      Iterable<ByteArray> keys, Map<ByteArray, byte[]> transforms, long getAllOpTimeoutInMs)
      throws VoldemortException {
    StoreUtils.assertValidKeys(keys);

    long startTimeMs = -1;
    long startTimeNs = -1;

    if (logger.isDebugEnabled()) {
      startTimeMs = System.currentTimeMillis();
      startTimeNs = System.nanoTime();
    }

    boolean allowReadRepair = repairReads && (transforms == null || transforms.size() == 0);

    GetAllPipelineData pipelineData = new GetAllPipelineData();
    if (zoneRoutingEnabled) pipelineData.setZonesRequired(storeDef.getZoneCountReads());
    else pipelineData.setZonesRequired(null);
    pipelineData.setStats(stats);

    Pipeline pipeline = new Pipeline(Operation.GET_ALL, getAllOpTimeoutInMs, TimeUnit.MILLISECONDS);
    pipeline.addEventAction(
        Event.STARTED,
        new GetAllConfigureNodes(
            pipelineData,
            Event.CONFIGURED,
            failureDetector,
            storeDef.getPreferredReads(),
            storeDef.getRequiredReads(),
            routingStrategy,
            keys,
            transforms,
            clientZone,
            zoneAffinity));
    pipeline.addEventAction(
        Event.CONFIGURED,
        new PerformParallelGetAllRequests(
            pipelineData,
            Event.INSUFFICIENT_SUCCESSES,
            failureDetector,
            getAllOpTimeoutInMs,
            nonblockingStores));
    pipeline.addEventAction(
        Event.INSUFFICIENT_SUCCESSES,
        new PerformSerialGetAllRequests(
            pipelineData,
            allowReadRepair ? Event.RESPONSES_RECEIVED : Event.COMPLETED,
            keys,
            failureDetector,
            innerStores,
            storeDef.getPreferredReads(),
            storeDef.getRequiredReads(),
            timeoutConfig.isPartialGetAllAllowed()));

    if (allowReadRepair)
      pipeline.addEventAction(
          Event.RESPONSES_RECEIVED,
          new GetAllReadRepair(
              pipelineData,
              Event.COMPLETED,
              storeDef.getPreferredReads(),
              getAllOpTimeoutInMs,
              nonblockingStores,
              readRepairer));

    pipeline.addEvent(Event.STARTED);

    if (logger.isDebugEnabled()) {
      StringBuilder keyStr = new StringBuilder();
      for (ByteArray key : keys) {
        keyStr.append(ByteUtils.toHexString(key.get()) + ",");
      }
      logger.debug(
          "Operation " + pipeline.getOperation().getSimpleName() + " Keys " + keyStr.toString());
    }
    try {
      pipeline.execute();
    } catch (VoldemortException e) {
      stats.reportException(e);
      throw e;
    }

    if (pipelineData.getFatalError() != null) throw pipelineData.getFatalError();

    if (logger.isDebugEnabled()) {
      logger.debug(
          "Finished "
              + pipeline.getOperation().getSimpleName()
              + "for keys "
              + ByteArray.toHexStrings(keys)
              + " keyRef: "
              + System.identityHashCode(keys)
              + "; started at "
              + startTimeMs
              + " took "
              + (System.nanoTime() - startTimeNs)
              + " values: "
              + formatNodeValuesFromGetAll(pipelineData.getResponses()));
    }

    return pipelineData.getResult();
  }
  public List<Versioned<byte[]>> get(
      final ByteArray key, final byte[] transforms, final long getOpTimeout) {
    StoreUtils.assertValidKey(key);

    long startTimeMs = -1;
    long startTimeNs = -1;

    if (logger.isDebugEnabled()) {
      startTimeMs = System.currentTimeMillis();
      startTimeNs = System.nanoTime();
    }

    BasicPipelineData<List<Versioned<byte[]>>> pipelineData =
        new BasicPipelineData<List<Versioned<byte[]>>>();
    if (zoneRoutingEnabled) pipelineData.setZonesRequired(storeDef.getZoneCountReads());
    else pipelineData.setZonesRequired(null);
    pipelineData.setStats(stats);

    final Pipeline pipeline = new Pipeline(Operation.GET, getOpTimeout, TimeUnit.MILLISECONDS);
    boolean allowReadRepair = repairReads && transforms == null;

    StoreRequest<List<Versioned<byte[]>>> blockingStoreRequest =
        new StoreRequest<List<Versioned<byte[]>>>() {

          @Override
          public List<Versioned<byte[]>> request(Store<ByteArray, byte[], byte[]> store) {
            return store.get(key, transforms);
          }
        };

    // Get the correct type of configure nodes action depending on the store
    // requirements
    AbstractConfigureNodes<
            ByteArray, List<Versioned<byte[]>>, BasicPipelineData<List<Versioned<byte[]>>>>
        configureNodes = makeNodeConfigurationForGet(pipelineData, key);

    pipeline.addEventAction(Event.STARTED, configureNodes);

    pipeline.addEventAction(
        Event.CONFIGURED,
        new PerformParallelRequests<
            List<Versioned<byte[]>>, BasicPipelineData<List<Versioned<byte[]>>>>(
            pipelineData,
            allowReadRepair ? Event.RESPONSES_RECEIVED : Event.COMPLETED,
            key,
            transforms,
            failureDetector,
            storeDef.getPreferredReads(),
            storeDef.getRequiredReads(),
            getOpTimeout,
            nonblockingStores,
            Event.INSUFFICIENT_SUCCESSES,
            Event.INSUFFICIENT_ZONES));
    pipeline.addEventAction(
        Event.INSUFFICIENT_SUCCESSES,
        new PerformSerialRequests<
            List<Versioned<byte[]>>, BasicPipelineData<List<Versioned<byte[]>>>>(
            pipelineData,
            allowReadRepair ? Event.RESPONSES_RECEIVED : Event.COMPLETED,
            key,
            failureDetector,
            innerStores,
            storeDef.getPreferredReads(),
            storeDef.getRequiredReads(),
            blockingStoreRequest,
            null));

    if (allowReadRepair)
      pipeline.addEventAction(
          Event.RESPONSES_RECEIVED,
          new ReadRepair<BasicPipelineData<List<Versioned<byte[]>>>>(
              pipelineData,
              Event.COMPLETED,
              storeDef.getPreferredReads(),
              getOpTimeout,
              nonblockingStores,
              readRepairer));

    if (zoneRoutingEnabled)
      pipeline.addEventAction(
          Event.INSUFFICIENT_ZONES,
          new PerformZoneSerialRequests<
              List<Versioned<byte[]>>, BasicPipelineData<List<Versioned<byte[]>>>>(
              pipelineData,
              allowReadRepair ? Event.RESPONSES_RECEIVED : Event.COMPLETED,
              key,
              failureDetector,
              innerStores,
              blockingStoreRequest));

    pipeline.addEvent(Event.STARTED);

    if (logger.isDebugEnabled()) {
      logger.debug(
          "Operation "
              + pipeline.getOperation().getSimpleName()
              + " Key "
              + ByteUtils.toHexString(key.get()));
    }

    try {
      pipeline.execute();
    } catch (VoldemortException e) {
      stats.reportException(e);
      throw e;
    }

    if (pipelineData.getFatalError() != null) throw pipelineData.getFatalError();

    List<Versioned<byte[]>> results = new ArrayList<Versioned<byte[]>>();

    for (Response<ByteArray, List<Versioned<byte[]>>> response : pipelineData.getResponses()) {
      List<Versioned<byte[]>> value = response.getValue();

      if (value != null) results.addAll(value);
    }

    if (logger.isDebugEnabled()) {
      logger.debug(
          "Finished "
              + pipeline.getOperation().getSimpleName()
              + " for key "
              + ByteUtils.toHexString(key.get())
              + " keyRef: "
              + System.identityHashCode(key)
              + "; started at "
              + startTimeMs
              + " took "
              + (System.nanoTime() - startTimeNs)
              + " values: "
              + formatNodeValuesFromGet(pipelineData.getResponses()));
    }

    return results;
  }