public StorageService(
      StoreRepository storeRepository,
      MetadataStore metadata,
      SchedulerService scheduler,
      VoldemortConfig config) {
    super(ServiceType.STORAGE);
    this.voldemortConfig = config;
    this.scheduler = scheduler;
    this.storeRepository = storeRepository;
    this.metadata = metadata;
    this.scanPermitWrapper = new ScanPermitWrapper(voldemortConfig.getNumScanPermits());
    this.storageConfigs = new ConcurrentHashMap<String, StorageConfiguration>();
    this.clientThreadPool =
        new ClientThreadPool(
            config.getClientMaxThreads(),
            config.getClientThreadIdleMs(),
            config.getClientMaxQueuedRequests());
    this.storeFactory =
        new ClientRequestExecutorPool(
            config.getClientSelectors(),
            config.getClientMaxConnectionsPerNode(),
            config.getClientConnectionTimeoutMs(),
            config.getSocketTimeoutMs(),
            config.getSocketBufferSize(),
            config.getSocketKeepAlive());

    FailureDetectorConfig failureDetectorConfig =
        new FailureDetectorConfig(voldemortConfig)
            .setCluster(metadata.getCluster())
            .setStoreVerifier(new ServerStoreVerifier(storeFactory, metadata, config));
    this.failureDetector = create(failureDetectorConfig, config.isJmxEnabled());
    this.storeStats = new StoreStats();
    this.routedStoreFactory = new RoutedStoreFactory();
    this.routedStoreFactory.setThreadPool(this.clientThreadPool);
    this.routedStoreConfig =
        new RoutedStoreConfig(this.voldemortConfig, this.metadata.getCluster());

    /*
     * Initialize the dynamic throttle limit based on the per node limit
     * config only if read-only engine is being used.
     */
    if (this.voldemortConfig
        .getStorageConfigurations()
        .contains(ReadOnlyStorageConfiguration.class.getName())) {
      long rate = this.voldemortConfig.getReadOnlyFetcherMaxBytesPerSecond();
      this.dynThrottleLimit = new DynamicThrottleLimit(rate);
    } else this.dynThrottleLimit = null;

    // create the proxy put thread pool
    this.proxyPutWorkerPool =
        Executors.newFixedThreadPool(
            config.getMaxProxyPutThreads(), new DaemonThreadFactory("voldemort-proxy-put-thread"));
    this.aggregatedProxyPutStats = new ProxyPutStats(null);
    if (config.isJmxEnabled()) {
      JmxUtils.registerMbean(
          this.aggregatedProxyPutStats,
          JmxUtils.createObjectName("voldemort.store.rebalancing", "aggregate-proxy-puts"));
    }

    this.aggregatedQuotaStats = new QuotaLimitStats(null);
    if (config.isJmxEnabled()) {
      JmxUtils.registerMbean(
          this.aggregatedQuotaStats,
          JmxUtils.createObjectName("voldemort.store.quota", "aggregate-quota-limit-stats"));
    }
  }
  public StorageService(
      StoreRepository storeRepository,
      MetadataStore metadata,
      SchedulerService scheduler,
      VoldemortConfig config) {
    super(ServiceType.STORAGE);
    this.voldemortConfig = config;
    this.scheduler = scheduler;
    this.storeRepository = storeRepository;
    this.metadata = metadata;
    this.scanPermitWrapper = new ScanPermitWrapper(voldemortConfig.getNumScanPermits());
    this.storageConfigs = new ConcurrentHashMap<String, StorageConfiguration>();
    this.clientThreadPool =
        new ClientThreadPool(
            config.getClientMaxThreads(),
            config.getClientThreadIdleMs(),
            config.getClientMaxQueuedRequests());
    this.storeFactory =
        new ClientRequestExecutorPool(
            config.getClientSelectors(),
            config.getClientMaxConnectionsPerNode(),
            config.getClientConnectionTimeoutMs(),
            config.getSocketTimeoutMs(),
            config.getSocketBufferSize(),
            config.getSocketKeepAlive(),
            "-storage");

    FailureDetectorConfig failureDetectorConfig =
        new FailureDetectorConfig(voldemortConfig)
            .setCluster(metadata.getCluster())
            .setConnectionVerifier(
                new ServerStoreConnectionVerifier(storeFactory, metadata, config));
    FailureDetectorConfig slopStreamingFailureDetectorConfig =
        new FailureDetectorConfig(voldemortConfig)
            .setImplementationClassName(AsyncRecoveryFailureDetector.class.getName())
            .setCluster(metadata.getCluster())
            .setConnectionVerifier(new AdminSlopStreamingVerifier(this.metadata.getCluster()));
    this.failureDetector = create(failureDetectorConfig, config.isJmxEnabled());
    this.slopStreamingFailureDetector =
        create(slopStreamingFailureDetectorConfig, config.isJmxEnabled());
    this.storeStats = new StoreStats("aggregate.storage-service");
    this.routedStoreFactory = new RoutedStoreFactory();
    this.routedStoreFactory.setThreadPool(this.clientThreadPool);
    this.routedStoreConfig =
        new RoutedStoreConfig(this.voldemortConfig, this.metadata.getCluster());

    // create the proxy put thread pool
    this.proxyPutWorkerPool =
        Executors.newFixedThreadPool(
            config.getMaxProxyPutThreads(), new DaemonThreadFactory("voldemort-proxy-put-thread"));
    this.aggregatedProxyPutStats = new ProxyPutStats(null);
    if (config.isJmxEnabled()) {
      JmxUtils.registerMbean(
          this.aggregatedProxyPutStats,
          JmxUtils.createObjectName("voldemort.store.rebalancing", "aggregate-proxy-puts"));
    }

    this.aggregatedQuotaStats = new QuotaLimitStats(null);
    if (config.isJmxEnabled()) {
      JmxUtils.registerMbean(
          this.aggregatedQuotaStats,
          JmxUtils.createObjectName("voldemort.store.quota", "aggregate-quota-limit-stats"));
    }
  }