@Override
  public void close() {
    VoldemortException exception = null;

    for (NonblockingStore store : nonblockingStores.values()) {
      try {
        store.close();
      } catch (VoldemortException e) {
        exception = e;
      }
    }

    if (this.jmxEnabled) {
      JmxUtils.unregisterMbean(
          JmxUtils.createObjectName(
              JmxUtils.getPackageName(stats.getClass()), getName() + identifierString));
    }

    if (exception != null) throw exception;

    super.close();
  }
  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;
  }
  /**
   * Create a PipelineRoutedStore
   *
   * @param innerStores The mapping of node to client
   * @param nonblockingStores
   * @param slopStores The stores for hints
   * @param nonblockingSlopStores
   * @param cluster Cluster definition
   * @param storeDef Store definition
   */
  public PipelineRoutedStore(
      Map<Integer, Store<ByteArray, byte[], byte[]>> innerStores,
      Map<Integer, NonblockingStore> nonblockingStores,
      Map<Integer, Store<ByteArray, Slop, byte[]>> slopStores,
      Map<Integer, NonblockingStore> nonblockingSlopStores,
      Cluster cluster,
      StoreDefinition storeDef,
      FailureDetector failureDetector,
      boolean repairReads,
      TimeoutConfig timeoutConfig,
      int clientZoneId,
      boolean isJmxEnabled,
      String identifierString,
      ZoneAffinity zoneAffinity) {
    super(
        storeDef.getName(),
        innerStores,
        cluster,
        storeDef,
        repairReads,
        timeoutConfig,
        failureDetector,
        SystemTime.INSTANCE);
    if (zoneAffinity != null
        && storeDef.getZoneCountReads() != null
        && storeDef.getZoneCountReads() > 0) {
      if (zoneAffinity.isGetOpZoneAffinityEnabled()) {
        throw new IllegalArgumentException(
            "storeDef.getZoneCountReads() is non-zero while zoneAffinityGet is enabled");
      }
      if (zoneAffinity.isGetAllOpZoneAffinityEnabled()) {
        throw new IllegalArgumentException(
            "storeDef.getZoneCountReads() is non-zero while zoneAffinityGetAll is enabled");
      }
    }
    this.nonblockingSlopStores = nonblockingSlopStores;
    if (clientZoneId == Zone.UNSET_ZONE_ID) {
      Collection<Zone> availableZones = cluster.getZones();
      this.clientZone = availableZones.iterator().next();
      if (availableZones.size() > 1) {
        String format =
            "Client Zone is not specified. Default to Zone %d. The servers could be in a remote zone";
        logger.warn(String.format(format, this.clientZone.getId()));
      } else {
        if (logger.isDebugEnabled())
          logger.debug(
              String.format(
                  "Client Zone is not specified. Default to Zone %d", this.clientZone.getId()));
      }
    } else {
      this.clientZone = cluster.getZoneById(clientZoneId);
    }
    this.nonblockingStores = new ConcurrentHashMap<Integer, NonblockingStore>(nonblockingStores);
    this.slopStores = slopStores;
    if (storeDef.getRoutingStrategyType().compareTo(RoutingStrategyType.ZONE_STRATEGY) == 0) {
      zoneRoutingEnabled = true;
    } else {
      zoneRoutingEnabled = false;
    }
    if (storeDef.hasHintedHandoffStrategyType()) {
      HintedHandoffStrategyFactory factory =
          new HintedHandoffStrategyFactory(zoneRoutingEnabled, clientZone.getId());
      this.handoffStrategy = factory.updateHintedHandoffStrategy(storeDef, cluster);
    } else {
      this.handoffStrategy = null;
    }

    this.jmxEnabled = isJmxEnabled;
    this.identifierString = identifierString;
    if (this.jmxEnabled) {
      stats = new PipelineRoutedStats();
      JmxUtils.registerMbean(
          stats,
          JmxUtils.createObjectName(
              JmxUtils.getPackageName(stats.getClass()), getName() + identifierString));
    }
    if (zoneAffinity != null) {
      this.zoneAffinity = zoneAffinity;
    } else {
      this.zoneAffinity = new ZoneAffinity();
    }
  }