private ConfigureNodesType obtainNodeConfigurationType(
      Integer zonesRequired, Operation operation) {
    if (operation == Operation.GET && zoneAffinity.isGetOpZoneAffinityEnabled()) {
      return ConfigureNodesType.LOCAL_ZONE_ONLY;
    }

    if (zonesRequired != null) {
      if (routingStrategy.getType().equals(RoutingStrategyType.TO_ALL_LOCAL_PREF_STRATEGY)) {
        return ConfigureNodesType.BYZONE_LOCAL;
      } else {
        return ConfigureNodesType.BYZONE;
      }
    } else {
      if (routingStrategy.getType().equals(RoutingStrategyType.TO_ALL_LOCAL_PREF_STRATEGY)) {
        return ConfigureNodesType.DEFAULT_LOCAL;
      }
    }

    return ConfigureNodesType.DEFAULT;
  }
  @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;
  }
  /**
   * 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();
    }
  }