You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ab...@apache.org on 2017/10/04 14:43:41 UTC

[45/45] lucene-solr:jira/solr-11285-sim: SOLR-11285: Simulator impl fixes to catch up with API changes.

SOLR-11285: Simulator impl fixes to catch up with API changes.


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/a578baee
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/a578baee
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/a578baee

Branch: refs/heads/jira/solr-11285-sim
Commit: a578baee3216014457a93ea1b981bd7101d01f5e
Parents: dfd74a2
Author: Andrzej Bialecki <ab...@apache.org>
Authored: Wed Oct 4 16:42:12 2017 +0200
Committer: Andrzej Bialecki <ab...@apache.org>
Committed: Wed Oct 4 16:42:12 2017 +0200

----------------------------------------------------------------------
 .../autoscaling/sim/SimCloudDataProvider.java   |  87 -----
 .../cloud/autoscaling/sim/SimCloudManager.java  | 112 ++++++
 .../autoscaling/sim/SimClusterDataProvider.java | 380 -------------------
 .../sim/SimClusterStateProvider.java            | 331 ++++++++++++++++
 .../autoscaling/sim/SimDistribStateManager.java |  37 +-
 .../sim/SimDistributedQueueFactory.java         |  79 +++-
 .../autoscaling/sim/SimNodeStateProvider.java   |  69 ++++
 .../sim/TestClusterDataProvider.java            | 183 ---------
 .../sim/TestClusterStateProvider.java           | 181 +++++++++
 .../sim/TestDistribStateManager.java            |  11 +-
 .../sim/TestSimClusterDataProvider.java         |  77 ----
 .../sim/TestSimClusterStateProvider.java        |  78 ++++
 .../sim/TestSimDistributedQueue.java            |   2 +-
 13 files changed, 884 insertions(+), 743 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/a578baee/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/SimCloudDataProvider.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/SimCloudDataProvider.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/SimCloudDataProvider.java
deleted file mode 100644
index 62f6e1b..0000000
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/SimCloudDataProvider.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.solr.cloud.autoscaling.sim;
-
-import java.io.IOException;
-import java.util.Map;
-
-import org.apache.solr.client.solrj.SolrClient;
-import org.apache.solr.client.solrj.SolrRequest;
-import org.apache.solr.client.solrj.SolrResponse;
-import org.apache.solr.client.solrj.SolrServerException;
-import org.apache.solr.client.solrj.cloud.autoscaling.ClusterDataProvider;
-import org.apache.solr.client.solrj.cloud.autoscaling.DistribStateManager;
-import org.apache.solr.client.solrj.cloud.autoscaling.SolrCloudDataProvider;
-import org.apache.solr.client.solrj.request.CollectionAdminRequest;
-import org.apache.solr.client.solrj.response.SolrResponseBase;
-
-/**
- * Simulated {@link SolrCloudDataProvider}.
- */
-public class SimCloudDataProvider implements SolrCloudDataProvider {
-
-  private final SimDistribStateManager stateManager;
-  private final SimClusterDataProvider dataProvider;
-  private final SimDistributedQueueFactory queueFactory;
-  private SolrClient solrClient;
-  private final SimHttpServer httpServer;
-
-  public SimCloudDataProvider() {
-    this.stateManager = new SimDistribStateManager();
-    this.dataProvider = new SimClusterDataProvider();
-    this.queueFactory = new SimDistributedQueueFactory();
-    this.httpServer = new SimHttpServer();
-  }
-
-  public void setSolrClient(SolrClient solrClient) {
-    this.solrClient = solrClient;
-  }
-
-  @Override
-  public ClusterDataProvider getClusterDataProvider() {
-    return dataProvider;
-  }
-
-  @Override
-  public DistribStateManager getDistribStateManager() {
-    return stateManager;
-  }
-
-  @Override
-  public DistributedQueueFactory getDistributedQueueFactory() {
-    return queueFactory;
-  }
-
-  @Override
-  public SolrResponse request(SolrRequest req) throws IOException {
-    if (solrClient != null) {
-      try {
-        return req.process(solrClient);
-      } catch (SolrServerException e) {
-        throw new IOException(e);
-      }
-    } else {
-      return dataProvider.simHandleSolrRequest(req);
-    }
-  }
-
-  @Override
-  public byte[] httpRequest(String url, SolrRequest.METHOD method, Map<String, String> headers, String payload, int timeout, boolean followRedirects) throws IOException {
-    return httpServer.httpRequest(url, method, headers, payload, timeout, followRedirects);
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/a578baee/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/SimCloudManager.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/SimCloudManager.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/SimCloudManager.java
new file mode 100644
index 0000000..c683003
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/SimCloudManager.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.cloud.autoscaling.sim;
+
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.solr.client.solrj.SolrClient;
+import org.apache.solr.client.solrj.SolrRequest;
+import org.apache.solr.client.solrj.SolrResponse;
+import org.apache.solr.client.solrj.SolrServerException;
+import org.apache.solr.client.solrj.cloud.autoscaling.DistribStateManager;
+import org.apache.solr.client.solrj.cloud.autoscaling.NodeStateProvider;
+import org.apache.solr.client.solrj.cloud.autoscaling.SolrCloudManager;
+import org.apache.solr.client.solrj.impl.ClusterStateProvider;
+
+/**
+ * Simulated {@link SolrCloudManager}.
+ */
+public class SimCloudManager implements SolrCloudManager {
+
+  private final SimDistribStateManager stateManager;
+  private final SimClusterStateProvider clusterStateProvider;
+  private final SimNodeStateProvider nodeStateProvider;
+  private final SimDistributedQueueFactory queueFactory;
+  private SolrClient solrClient;
+  private final SimHttpServer httpServer;
+
+  public SimCloudManager() {
+    this.stateManager = new SimDistribStateManager();
+    this.clusterStateProvider = new SimClusterStateProvider();
+    this.nodeStateProvider = new SimNodeStateProvider(this.clusterStateProvider, null);
+    this.queueFactory = new SimDistributedQueueFactory();
+    this.httpServer = new SimHttpServer();
+  }
+
+  public void setSolrClient(SolrClient solrClient) {
+    this.solrClient = solrClient;
+  }
+
+  // ---------- type-safe methods to obtain simulator components ----------
+  public SimClusterStateProvider getSimClusterStateProvider() {
+    return clusterStateProvider;
+  }
+
+  public SimNodeStateProvider getSimNodeStateProvider() {
+    return nodeStateProvider;
+  }
+
+  public SimDistribStateManager getSimDistribStateManager() {
+    return stateManager;
+  }
+
+  public SimDistributedQueueFactory getSimDistributedQueueFactory() {
+    return queueFactory;
+  }
+
+  // --------- interface methods -----------
+
+  @Override
+  public ClusterStateProvider getClusterStateProvider() {
+    return clusterStateProvider;
+  }
+
+  @Override
+  public NodeStateProvider getNodeStateProvider() {
+    return nodeStateProvider;
+  }
+
+  @Override
+  public DistribStateManager getDistribStateManager() {
+    return stateManager;
+  }
+
+  @Override
+  public DistributedQueueFactory getDistributedQueueFactory() {
+    return queueFactory;
+  }
+
+  @Override
+  public SolrResponse request(SolrRequest req) throws IOException {
+    if (solrClient != null) {
+      try {
+        return req.process(solrClient);
+      } catch (SolrServerException e) {
+        throw new IOException(e);
+      }
+    } else {
+      return clusterStateProvider.simHandleSolrRequest(req);
+    }
+  }
+
+  @Override
+  public byte[] httpRequest(String url, SolrRequest.METHOD method, Map<String, String> headers, String payload, int timeout, boolean followRedirects) throws IOException {
+    return httpServer.httpRequest(url, method, headers, payload, timeout, followRedirects);
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/a578baee/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/SimClusterDataProvider.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/SimClusterDataProvider.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/SimClusterDataProvider.java
deleted file mode 100644
index 4b7ab91..0000000
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/SimClusterDataProvider.java
+++ /dev/null
@@ -1,380 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.solr.cloud.autoscaling.sim;
-
-import java.io.IOException;
-import java.lang.invoke.MethodHandles;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.stream.Collectors;
-
-import org.apache.solr.client.solrj.SolrRequest;
-import org.apache.solr.client.solrj.SolrResponse;
-import org.apache.solr.client.solrj.cloud.autoscaling.AutoScalingConfig;
-import org.apache.solr.client.solrj.cloud.autoscaling.ClusterDataProvider;
-import org.apache.solr.client.solrj.cloud.autoscaling.ReplicaInfo;
-import org.apache.solr.client.solrj.request.CollectionAdminRequest;
-import org.apache.solr.client.solrj.response.SolrResponseBase;
-import org.apache.solr.common.SolrException;
-import org.apache.solr.common.cloud.ClusterState;
-import org.apache.solr.common.cloud.DocCollection;
-import org.apache.solr.common.cloud.DocRouter;
-import org.apache.solr.common.cloud.Replica;
-import org.apache.solr.common.cloud.Slice;
-import org.apache.solr.common.cloud.ZkStateReader;
-import org.apache.solr.common.params.CollectionParams;
-import org.apache.solr.common.params.CoreAdminParams;
-import org.apache.solr.common.params.SolrParams;
-import org.apache.solr.handler.admin.CollectionsHandler;
-import org.apache.zookeeper.WatchedEvent;
-import org.apache.zookeeper.Watcher;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- *
- */
-public class SimClusterDataProvider implements ClusterDataProvider {
-  private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
-
-  private final Map<String, List<ReplicaInfo>> nodes = new ConcurrentHashMap<>();
-  private final Map<String, Map<String, Object>> nodeValues = new ConcurrentHashMap<>();
-
-  private final List<Watcher> autoscalingWatchers = new ArrayList<>();
-  private final Map<String, Object> clusterProperties = new ConcurrentHashMap<>();
-  private final Map<String, Map<String, Object>> collProperties = new ConcurrentHashMap<>();
-  private final Map<String, Map<String, Map<String, Object>>> sliceProperties = new ConcurrentHashMap<>();
-
-  private AutoScalingConfig autoScalingConfig = new AutoScalingConfig(Collections.emptyMap());
-  private SimDistribStateManager distribStateManager;
-
-  /**
-   * Zero-arg constructor. The instance needs to be initialized using the <code>sim*</code> methods in order
-   * to ensure proper behavior, otherwise it will behave as a cluster with zero live nodes and zero replicas.
-   */
-  public SimClusterDataProvider() {
-
-  }
-
-  /**
-   * Construct simulated instance using initial cluster data.
-   * @param initialState cluster state
-   * @param autoScalingConfig autoscaling config
-   * @param nodeValues per-node values (eg. metrics)
-   */
-  public SimClusterDataProvider(ClusterState initialState, AutoScalingConfig autoScalingConfig, Map<String, Map<String, Object>> nodeValues) {
-    if (nodeValues != null) {
-      this.nodeValues.putAll(nodeValues);
-    }
-    if (autoScalingConfig != null) {
-      this.autoScalingConfig = (AutoScalingConfig)autoScalingConfig.clone();
-    }
-    simSetClusterState(initialState);
-  }
-
-  // ============== SIMULATOR SETUP METHODS ====================
-
-  public void simSetClusterState(ClusterState initialState) {
-    collProperties.clear();
-    sliceProperties.clear();
-    nodes.clear();
-    Collection<String> liveNodes = initialState.getLiveNodes();
-    initialState.forEachCollection(dc -> {
-      collProperties.computeIfAbsent(dc.getName(), name -> new HashMap<>()).putAll(dc.getProperties());
-      dc.getSlices().forEach(s -> {
-        sliceProperties.computeIfAbsent(dc.getName(), name -> new HashMap<>())
-            .computeIfAbsent(s.getName(), name -> new HashMap<>()).putAll(s.getProperties());
-        s.getReplicas().forEach(r -> {
-          ReplicaInfo ri = new ReplicaInfo(r.getName(), dc.getName(), s.getName(), r.getType(), r.getProperties());
-          if (liveNodes.contains(r.getNodeName())) {
-            nodes.computeIfAbsent(r.getNodeName(), rn -> new ArrayList<>()).add(ri);
-          }
-        });
-      });
-    });
-    nodeValues.keySet().retainAll(liveNodes);
-  }
-
-  public void simSetDistribStateManager(SimDistribStateManager simDistribStateManager) {
-    this.distribStateManager = simDistribStateManager;
-  }
-
-  public void simAddNodes(Collection<String> nodeIds) throws Exception {
-    for (String node : nodeIds) {
-      if (nodes.containsKey(node)) {
-        throw new Exception("Node " + node + " already exists");
-      }
-    }
-    for (String node : nodeIds) {
-      simAddNode(node);
-    }
-  }
-
-  // todo: maybe hook up DistribStateManager /live_nodes ?
-  // todo: maybe hook up DistribStateManager /clusterstate.json watchers?
-  public boolean simAddNode(String nodeId) throws Exception {
-    if (nodes.containsKey(nodeId)) {
-      throw new Exception("Node " + nodeId + " already exists");
-    }
-    return nodes.putIfAbsent(nodeId, new ArrayList<>()) == null;
-  }
-
-  // todo: maybe hook up DistribStateManager /live_nodes ?
-  // todo: maybe hook up DistribStateManager /clusterstate.json watchers?
-  public boolean simRemoveNode(String nodeId) {
-    nodes.remove(nodeId);
-    return nodeValues.remove(nodeId) != null;
-  }
-
-  // todo: maybe hook up DistribStateManager /clusterstate.json watchers?
-  public void simAddReplica(String nodeId, ReplicaInfo replicaInfo) throws Exception {
-    // make sure coreNodeName is unique across cluster
-    for (Map.Entry<String, List<ReplicaInfo>> e : nodes.entrySet()) {
-      for (ReplicaInfo ri : e.getValue()) {
-        if (ri.getCore().equals(replicaInfo.getCore())) {
-          throw new Exception("Duplicate coreNodeName for existing=" + ri + " on node " + e.getKey() + " and new=" + replicaInfo);
-        }
-      }
-    }
-    List<ReplicaInfo> replicas = nodes.computeIfAbsent(nodeId, n -> new ArrayList<>());
-    replicas.add(replicaInfo);
-  }
-
-  // todo: maybe hook up DistribStateManager /clusterstate.json watchers?
-  public void simRemoveReplica(String nodeId, String coreNodeName) throws Exception {
-    List<ReplicaInfo> replicas = nodes.computeIfAbsent(nodeId, n -> new ArrayList<>());
-    for (int i = 0; i < replicas.size(); i++) {
-      if (coreNodeName.equals(replicas.get(i).getCore())) {
-        replicas.remove(i);
-        return;
-      }
-    }
-    throw new Exception("Replica " + coreNodeName + " not found on node " + nodeId);
-  }
-
-  public void simSetNodeValues(String node, Map<String, Object> values) {
-    nodeValues.put(node, new ConcurrentHashMap<>(values));
-  }
-
-  public void simSetNodeValue(String node, String key, Object value) {
-    nodeValues.computeIfAbsent(node, n -> new ConcurrentHashMap<>()).put(key, value);
-  }
-
-  // todo: maybe hook up DistribStateManager /clusterstate.json watchers?
-  public void simSetClusterProperties(Map<String, Object> properties) {
-    clusterProperties.clear();
-    if (properties != null) {
-      this.clusterProperties.putAll(properties);
-    }
-  }
-
-  public void simSetClusterProperty(String key, Object value) {
-    if (value != null) {
-      clusterProperties.put(key, value);
-    } else {
-      clusterProperties.remove(key);
-    }
-  }
-
-  // todo: maybe hook up DistribStateManager /clusterstate.json watchers?
-  public void simSetCollectionProperties(String coll, Map<String, Object> properties) {
-    if (properties == null) {
-      collProperties.remove(coll);
-    } else {
-      Map<String, Object> props = collProperties.computeIfAbsent(coll, c -> new HashMap<>());
-      props.clear();
-      props.putAll(properties);
-    }
-  }
-
-  public void simSetCollectionProperty(String coll, String key, String value) {
-    Map<String, Object> props = collProperties.computeIfAbsent(coll, c -> new HashMap<>());
-    if (value == null) {
-      props.remove(key);
-    } else {
-      props.put(key, value);
-    }
-  }
-
-  public void setSliceProperties(String coll, String slice, Map<String, Object> properties) {
-    Map<String, Object> sliceProps = sliceProperties.computeIfAbsent(coll, c -> new HashMap<>()).computeIfAbsent(slice, s -> new HashMap<>());
-    sliceProps.clear();
-    if (properties != null) {
-      sliceProps.putAll(properties);
-    }
-  }
-
-  // todo: maybe hook up DistribStateManager /autoscaling.json watchers?
-  public void simSetAutoScalingConfig(AutoScalingConfig autoScalingConfig) {
-    this.autoScalingConfig = autoScalingConfig;
-    WatchedEvent ev = new WatchedEvent(Watcher.Event.EventType.NodeDataChanged, Watcher.Event.KeeperState.SyncConnected, ZkStateReader.SOLR_AUTOSCALING_CONF_PATH);
-    for (Watcher w : autoscalingWatchers) {
-      w.process(ev);
-    }
-    autoscalingWatchers.clear();
-  }
-
-  public SolrResponse simHandleSolrRequest(SolrRequest req) throws IOException {
-    // support only a specific subset of collection admin ops
-    if (!(req instanceof CollectionAdminRequest)) {
-      throw new UnsupportedOperationException("Only CollectionAdminRequest-s are supported: " + req.getClass().getName());
-    }
-    SolrParams params = req.getParams();
-    String a = params.get(CoreAdminParams.ACTION);
-    SolrResponse rsp = new SolrResponseBase();
-    if (a != null) {
-      CollectionParams.CollectionAction action = CollectionParams.CollectionAction.get(a);
-      if (action == null) {
-        throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Unknown action: " + a);
-      }
-      switch (action) {
-        case ADDREPLICA:
-          break;
-        case ADDREPLICAPROP:
-          break;
-        case CREATE:
-          break;
-        case CLUSTERPROP:
-          break;
-        case CREATESHARD:
-          break;
-        case DELETE:
-          break;
-        case DELETENODE:
-          break;
-        case DELETEREPLICA:
-          break;
-        case DELETEREPLICAPROP:
-          break;
-        case DELETESHARD:
-          break;
-        case FORCELEADER:
-          break;
-        case LIST:
-          break;
-        case MODIFYCOLLECTION:
-          break;
-        case MOVEREPLICA:
-          break;
-        case REBALANCELEADERS:
-          break;
-        case RELOAD:
-          break;
-        case REPLACENODE:
-          break;
-        case SPLITSHARD:
-          break;
-        default:
-          throw new UnsupportedOperationException("Unsupported collection admin action=" + action);
-      }
-      LOG.info("Invoked Collection Action :{} with params {}", action.toLower(), req.getParams().toQueryString());
-    } else {
-      throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "action is a required param");
-    }
-
-    return rsp;
-
-  }
-
-  // interface methods
-
-  @Override
-  public Map<String, Object> getNodeValues(String node, Collection<String> tags) {
-    Map<String, Object> values = nodeValues.get(node);
-    if (values == null) {
-      return Collections.emptyMap();
-    }
-    return values.entrySet().stream().filter(e -> tags.contains(e.getKey())).collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue()));
-  }
-
-  @Override
-  public Map<String, Map<String, List<ReplicaInfo>>> getReplicaInfo(String node, Collection<String> keys) {
-    List<ReplicaInfo> replicas = nodes.get(node);
-    if (replicas == null) {
-      return Collections.emptyMap();
-    }
-    Map<String, Map<String, List<ReplicaInfo>>> res = new HashMap<>();
-    for (ReplicaInfo r : replicas) {
-      Map<String, List<ReplicaInfo>> perCollection = res.computeIfAbsent(r.getCollection(), s -> new HashMap<>());
-      List<ReplicaInfo> perShard = perCollection.computeIfAbsent(r.getShard(), s -> new ArrayList<>());
-      perShard.add(r);
-    }
-    return res;
-  }
-
-  @Override
-  public Collection<String> getLiveNodes() {
-    return nodes.keySet();
-  }
-
-  @Override
-  public ClusterState getClusterState() throws IOException {
-    return new ClusterState(0, nodes.keySet(), getCollectionStates());
-  }
-
-  private Map<String, DocCollection> getCollectionStates() {
-    Map<String, Map<String, Map<String, Replica>>> collMap = new HashMap<>();
-    nodes.forEach((n, replicas) -> {
-      replicas.forEach(ri -> {
-        Map<String, Object> props = new HashMap<>(ri.getVariables());
-        props.put(ZkStateReader.NODE_NAME_PROP, n);
-        Replica r = new Replica(ri.getName(), props);
-        collMap.computeIfAbsent(ri.getCollection(), c -> new HashMap<>())
-            .computeIfAbsent(ri.getShard(), s -> new HashMap<>())
-            .put(ri.getName(), r);
-      });
-    });
-    Map<String, DocCollection> res = new HashMap<>();
-    collMap.forEach((coll, shards) -> {
-      Map<String, Slice> slices = new HashMap<>();
-      shards.forEach((s, replicas) -> {
-        Map<String, Object> sliceProps = sliceProperties.computeIfAbsent(coll, c -> new HashMap<>()).computeIfAbsent(s, sl -> new HashMap<>());
-        Slice slice = new Slice(s, replicas, sliceProps);
-        slices.put(s, slice);
-      });
-      Map<String, Object> collProps = collProperties.computeIfAbsent(coll, c -> new HashMap<>());
-      DocCollection dc = new DocCollection(coll, slices, collProps, DocRouter.DEFAULT, 0, ZkStateReader.CLUSTER_STATE);
-      res.put(coll, dc);
-    });
-    return res;
-  }
-
-  @Override
-  public Map<String, Object> getClusterProperties() {
-    return clusterProperties;
-  }
-
-  @Override
-  public AutoScalingConfig getAutoScalingConfig(Watcher watcher) throws InterruptedException, IOException {
-    if (watcher != null && !autoscalingWatchers.contains(watcher)) {
-      autoscalingWatchers.add(watcher);
-    }
-    return autoScalingConfig;
-  }
-
-  @Override
-  public String getPolicyNameByCollection(String coll) {
-    Map<String, Object> props = collProperties.computeIfAbsent(coll, c -> new HashMap<>());
-    return (String)props.get("policy");
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/a578baee/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/SimClusterStateProvider.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/SimClusterStateProvider.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/SimClusterStateProvider.java
new file mode 100644
index 0000000..bce019c
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/SimClusterStateProvider.java
@@ -0,0 +1,331 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.cloud.autoscaling.sim;
+
+import java.io.IOException;
+import java.lang.invoke.MethodHandles;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.solr.client.solrj.SolrRequest;
+import org.apache.solr.client.solrj.SolrResponse;
+import org.apache.solr.client.solrj.cloud.autoscaling.ReplicaInfo;
+import org.apache.solr.client.solrj.impl.ClusterStateProvider;
+import org.apache.solr.client.solrj.request.CollectionAdminRequest;
+import org.apache.solr.client.solrj.response.SolrResponseBase;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.cloud.ClusterState;
+import org.apache.solr.common.cloud.DocCollection;
+import org.apache.solr.common.cloud.DocRouter;
+import org.apache.solr.common.cloud.Replica;
+import org.apache.solr.common.cloud.Slice;
+import org.apache.solr.common.cloud.ZkStateReader;
+import org.apache.solr.common.params.CollectionParams;
+import org.apache.solr.common.params.CoreAdminParams;
+import org.apache.solr.common.params.SolrParams;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ *
+ */
+public class SimClusterStateProvider implements ClusterStateProvider {
+  private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+  private final Map<String, List<ReplicaInfo>> nodeReplicaMap = new ConcurrentHashMap<>();
+
+  private final Map<String, Object> clusterProperties = new ConcurrentHashMap<>();
+  private final Map<String, Map<String, Object>> collProperties = new ConcurrentHashMap<>();
+  private final Map<String, Map<String, Map<String, Object>>> sliceProperties = new ConcurrentHashMap<>();
+
+  /**
+   * Zero-arg constructor. The instance needs to be initialized using the <code>sim*</code> methods in order
+   * to ensure proper behavior, otherwise it will behave as a cluster with zero live nodes and zero replicas.
+   */
+  public SimClusterStateProvider() {
+
+  }
+
+  // ============== SIMULATOR SETUP METHODS ====================
+
+  public void simSetClusterState(ClusterState initialState) {
+    collProperties.clear();
+    sliceProperties.clear();
+    nodeReplicaMap.clear();
+    Collection<String> liveNodes = initialState.getLiveNodes();
+    initialState.forEachCollection(dc -> {
+      collProperties.computeIfAbsent(dc.getName(), name -> new HashMap<>()).putAll(dc.getProperties());
+      dc.getSlices().forEach(s -> {
+        sliceProperties.computeIfAbsent(dc.getName(), name -> new HashMap<>())
+            .computeIfAbsent(s.getName(), name -> new HashMap<>()).putAll(s.getProperties());
+        s.getReplicas().forEach(r -> {
+          ReplicaInfo ri = new ReplicaInfo(r.getName(), dc.getName(), s.getName(), r.getProperties());
+          if (liveNodes.contains(r.getNodeName())) {
+            nodeReplicaMap.computeIfAbsent(r.getNodeName(), rn -> new ArrayList<>()).add(ri);
+          }
+        });
+      });
+    });
+  }
+
+  public void simAddNodes(Collection<String> nodeIds) throws Exception {
+    for (String node : nodeIds) {
+      if (nodeReplicaMap.containsKey(node)) {
+        throw new Exception("Node " + node + " already exists");
+      }
+    }
+    for (String node : nodeIds) {
+      simAddNode(node);
+    }
+  }
+
+  // todo: maybe hook up DistribStateManager /live_nodes ?
+  // todo: maybe hook up DistribStateManager /clusterstate.json watchers?
+  public boolean simAddNode(String nodeId) throws Exception {
+    if (nodeReplicaMap.containsKey(nodeId)) {
+      throw new Exception("Node " + nodeId + " already exists");
+    }
+    return nodeReplicaMap.putIfAbsent(nodeId, new ArrayList<>()) == null;
+  }
+
+  // todo: maybe hook up DistribStateManager /live_nodes ?
+  // todo: maybe hook up DistribStateManager /clusterstate.json watchers?
+  public boolean simRemoveNode(String nodeId) {
+    return nodeReplicaMap.remove(nodeId) != null;
+  }
+
+  // todo: maybe hook up DistribStateManager /clusterstate.json watchers?
+  public void simAddReplica(String nodeId, ReplicaInfo replicaInfo) throws Exception {
+    // make sure coreNodeName is unique across cluster
+    for (Map.Entry<String, List<ReplicaInfo>> e : nodeReplicaMap.entrySet()) {
+      for (ReplicaInfo ri : e.getValue()) {
+        if (ri.getCore().equals(replicaInfo.getCore())) {
+          throw new Exception("Duplicate coreNodeName for existing=" + ri + " on node " + e.getKey() + " and new=" + replicaInfo);
+        }
+      }
+    }
+    List<ReplicaInfo> replicas = nodeReplicaMap.computeIfAbsent(nodeId, n -> new ArrayList<>());
+    replicas.add(replicaInfo);
+  }
+
+  // todo: maybe hook up DistribStateManager /clusterstate.json watchers?
+  public void simRemoveReplica(String nodeId, String coreNodeName) throws Exception {
+    List<ReplicaInfo> replicas = nodeReplicaMap.computeIfAbsent(nodeId, n -> new ArrayList<>());
+    for (int i = 0; i < replicas.size(); i++) {
+      if (coreNodeName.equals(replicas.get(i).getCore())) {
+        replicas.remove(i);
+        return;
+      }
+    }
+    throw new Exception("Replica " + coreNodeName + " not found on node " + nodeId);
+  }
+
+  // todo: maybe hook up DistribStateManager /clusterstate.json watchers?
+  public void simSetClusterProperties(Map<String, Object> properties) {
+    clusterProperties.clear();
+    if (properties != null) {
+      this.clusterProperties.putAll(properties);
+    }
+  }
+
+  public void simSetClusterProperty(String key, Object value) {
+    if (value != null) {
+      clusterProperties.put(key, value);
+    } else {
+      clusterProperties.remove(key);
+    }
+  }
+
+  // todo: maybe hook up DistribStateManager /clusterstate.json watchers?
+  public void simSetCollectionProperties(String coll, Map<String, Object> properties) {
+    if (properties == null) {
+      collProperties.remove(coll);
+    } else {
+      Map<String, Object> props = collProperties.computeIfAbsent(coll, c -> new HashMap<>());
+      props.clear();
+      props.putAll(properties);
+    }
+  }
+
+  public void simSetCollectionProperty(String coll, String key, String value) {
+    Map<String, Object> props = collProperties.computeIfAbsent(coll, c -> new HashMap<>());
+    if (value == null) {
+      props.remove(key);
+    } else {
+      props.put(key, value);
+    }
+  }
+
+  public void setSliceProperties(String coll, String slice, Map<String, Object> properties) {
+    Map<String, Object> sliceProps = sliceProperties.computeIfAbsent(coll, c -> new HashMap<>()).computeIfAbsent(slice, s -> new HashMap<>());
+    sliceProps.clear();
+    if (properties != null) {
+      sliceProps.putAll(properties);
+    }
+  }
+
+  public SolrResponse simHandleSolrRequest(SolrRequest req) throws IOException {
+    // support only a specific subset of collection admin ops
+    if (!(req instanceof CollectionAdminRequest)) {
+      throw new UnsupportedOperationException("Only CollectionAdminRequest-s are supported: " + req.getClass().getName());
+    }
+    SolrParams params = req.getParams();
+    String a = params.get(CoreAdminParams.ACTION);
+    SolrResponse rsp = new SolrResponseBase();
+    if (a != null) {
+      CollectionParams.CollectionAction action = CollectionParams.CollectionAction.get(a);
+      if (action == null) {
+        throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Unknown action: " + a);
+      }
+      switch (action) {
+        case ADDREPLICA:
+          break;
+        case ADDREPLICAPROP:
+          break;
+        case CREATE:
+          break;
+        case CLUSTERPROP:
+          break;
+        case CREATESHARD:
+          break;
+        case DELETE:
+          break;
+        case DELETENODE:
+          break;
+        case DELETEREPLICA:
+          break;
+        case DELETEREPLICAPROP:
+          break;
+        case DELETESHARD:
+          break;
+        case FORCELEADER:
+          break;
+        case LIST:
+          break;
+        case MODIFYCOLLECTION:
+          break;
+        case MOVEREPLICA:
+          break;
+        case REBALANCELEADERS:
+          break;
+        case RELOAD:
+          break;
+        case REPLACENODE:
+          break;
+        case SPLITSHARD:
+          break;
+        default:
+          throw new UnsupportedOperationException("Unsupported collection admin action=" + action);
+      }
+      LOG.info("Invoked Collection Action :{} with params {}", action.toLower(), req.getParams().toQueryString());
+    } else {
+      throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "action is a required param");
+    }
+
+    return rsp;
+
+  }
+
+  public List<ReplicaInfo> simGetReplicaInfos(String node) {
+    return nodeReplicaMap.get(node);
+  }
+
+  // interface methods
+
+  @Override
+  public ClusterState.CollectionRef getState(String collection) {
+    try {
+      return getClusterState().getCollectionRef(collection);
+    } catch (IOException e) {
+      return null;
+    }
+  }
+
+  @Override
+  public Set<String> getLiveNodes() {
+    return nodeReplicaMap.keySet();
+  }
+
+  @Override
+  public String getAlias(String alias) {
+    return null;
+  }
+
+  @Override
+  public String getCollectionName(String name) {
+    return null;
+  }
+
+  @Override
+  public ClusterState getClusterState() throws IOException {
+    return new ClusterState(0, nodeReplicaMap.keySet(), getCollectionStates());
+  }
+
+  private Map<String, DocCollection> getCollectionStates() {
+    Map<String, Map<String, Map<String, Replica>>> collMap = new HashMap<>();
+    nodeReplicaMap.forEach((n, replicas) -> {
+      replicas.forEach(ri -> {
+        Map<String, Object> props = new HashMap<>(ri.getVariables());
+        props.put(ZkStateReader.NODE_NAME_PROP, n);
+        Replica r = new Replica(ri.getName(), props);
+        collMap.computeIfAbsent(ri.getCollection(), c -> new HashMap<>())
+            .computeIfAbsent(ri.getShard(), s -> new HashMap<>())
+            .put(ri.getName(), r);
+      });
+    });
+    Map<String, DocCollection> res = new HashMap<>();
+    collMap.forEach((coll, shards) -> {
+      Map<String, Slice> slices = new HashMap<>();
+      shards.forEach((s, replicas) -> {
+        Map<String, Object> sliceProps = sliceProperties.computeIfAbsent(coll, c -> new HashMap<>()).computeIfAbsent(s, sl -> new HashMap<>());
+        Slice slice = new Slice(s, replicas, sliceProps);
+        slices.put(s, slice);
+      });
+      Map<String, Object> collProps = collProperties.computeIfAbsent(coll, c -> new HashMap<>());
+      DocCollection dc = new DocCollection(coll, slices, collProps, DocRouter.DEFAULT, 0, ZkStateReader.CLUSTER_STATE);
+      res.put(coll, dc);
+    });
+    return res;
+  }
+
+  @Override
+  public Map<String, Object> getClusterProperties() {
+    return clusterProperties;
+  }
+
+  @Override
+  public String getPolicyNameByCollection(String coll) {
+    Map<String, Object> props = collProperties.computeIfAbsent(coll, c -> new HashMap<>());
+    return (String)props.get("policy");
+  }
+
+  @Override
+  public void connect() {
+
+  }
+
+  @Override
+  public void close() throws IOException {
+
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/a578baee/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/SimDistribStateManager.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/SimDistribStateManager.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/SimDistribStateManager.java
index 7c23a65..8871e02 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/SimDistribStateManager.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/SimDistribStateManager.java
@@ -21,6 +21,7 @@ import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -31,8 +32,13 @@ import java.util.concurrent.atomic.AtomicReference;
 import java.util.concurrent.locks.ReentrantLock;
 
 import org.apache.jute.Record;
+import org.apache.solr.client.solrj.cloud.autoscaling.AutoScalingConfig;
 import org.apache.solr.client.solrj.cloud.autoscaling.DistribStateManager;
+import org.apache.solr.client.solrj.cloud.autoscaling.VersionedData;
 import org.apache.solr.cloud.ActionThrottle;
+import org.apache.solr.common.cloud.ZkStateReader;
+import org.apache.solr.common.params.AutoScalingParams;
+import org.apache.solr.common.util.Utils;
 import org.apache.solr.util.IdUtils;
 import org.apache.zookeeper.CreateMode;
 import org.apache.zookeeper.KeeperException;
@@ -113,7 +119,7 @@ public class SimDistribStateManager implements DistribStateManager {
     public VersionedData getData(Watcher w) {
       dataLock.lock();
       try {
-        VersionedData res = new VersionedData(version, data);
+        VersionedData res = new VersionedData(version, data, clientId);
         if (w != null && !dataWatches.contains(w)) {
           dataWatches.add(w);
         }
@@ -433,7 +439,7 @@ public class SimDistribStateManager implements DistribStateManager {
             setData(rr.getPath(), rr.getData(), rr.getVersion());
             VersionedData vd = getData(rr.getPath());
             Stat s = new Stat();
-            s.setVersion(vd.version);
+            s.setVersion(vd.getVersion());
             res.add(new OpResult.SetDataResult(s));
           } else {
             throw new Exception("Unknown Op: " + op);
@@ -447,4 +453,31 @@ public class SimDistribStateManager implements DistribStateManager {
     }
     return res;
   }
+
+  @Override
+  public AutoScalingConfig getAutoScalingConfig(Watcher watcher) throws InterruptedException, IOException {
+    Map<String, Object> map = new HashMap<>();
+    int version = -1;
+    try {
+      VersionedData data = getData(ZkStateReader.SOLR_AUTOSCALING_CONF_PATH, watcher);
+      if (data != null && data.getData() != null && data.getData().length > 0) {
+        map = (Map<String, Object>) Utils.fromJSON(data.getData());
+      }
+    } catch (NoSuchElementException e) {
+      // ignore
+    }
+    map.put(AutoScalingParams.ZK_VERSION, version);
+    return new AutoScalingConfig(map);
+  }
+
+  // ------------ simulator methods --------------
+
+  public void simSetAutoScalingConfig(AutoScalingConfig cfg) throws Exception {
+    try {
+      makePath(ZkStateReader.SOLR_AUTOSCALING_CONF_PATH);
+    } catch (Exception e) {
+      // ignore
+    }
+    setData(ZkStateReader.SOLR_AUTOSCALING_CONF_PATH, Utils.toJSON(cfg), -1);
+  }
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/a578baee/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/SimDistributedQueueFactory.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/SimDistributedQueueFactory.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/SimDistributedQueueFactory.java
index a07a444..019e88c 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/SimDistributedQueueFactory.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/SimDistributedQueueFactory.java
@@ -2,7 +2,10 @@ package org.apache.solr.cloud.autoscaling.sim;
 
 import java.io.IOException;
 import java.lang.invoke.MethodHandles;
+import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
@@ -16,9 +19,11 @@ import java.util.concurrent.locks.Condition;
 import java.util.concurrent.locks.ReentrantLock;
 import java.util.function.Predicate;
 
+import com.codahale.metrics.Timer;
 import com.google.common.base.Preconditions;
 import org.apache.solr.client.solrj.cloud.DistributedQueue;
-import org.apache.solr.client.solrj.cloud.autoscaling.SolrCloudDataProvider;
+import org.apache.solr.client.solrj.cloud.autoscaling.SolrCloudManager;
+import org.apache.solr.cloud.Stats;
 import org.apache.solr.common.util.Pair;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -26,7 +31,7 @@ import org.slf4j.LoggerFactory;
 /**
  *
  */
-public class SimDistributedQueueFactory implements SolrCloudDataProvider.DistributedQueueFactory {
+public class SimDistributedQueueFactory implements SolrCloudManager.DistributedQueueFactory {
   private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
   Map<String, SimDistributedQueue> queues = new ConcurrentHashMap<>();
@@ -35,8 +40,8 @@ public class SimDistributedQueueFactory implements SolrCloudDataProvider.Distrib
   }
 
   @Override
-  public DistributedQueue makeQueue(String path) throws IOException {
-    return queues.computeIfAbsent(path, p -> new SimDistributedQueue());
+  public DistributedQueue makeQueue(final String path) throws IOException {
+    return queues.computeIfAbsent(path, p -> new SimDistributedQueue(path));
   }
 
   @Override
@@ -48,12 +53,23 @@ public class SimDistributedQueueFactory implements SolrCloudDataProvider.Distrib
     private final Queue<Pair<String, byte[]>> queue = new ConcurrentLinkedQueue<>();
     private final ReentrantLock updateLock = new ReentrantLock();
     private final Condition changed = updateLock.newCondition();
+    private final Stats stats = new Stats();
+    private final String dir;
     private int seq = 0;
 
+    public SimDistributedQueue(String dir) {
+      this.dir = dir;
+    }
+
     @Override
     public byte[] peek() throws Exception {
-      Pair<String, byte[]> pair = queue.peek();
-      return pair != null ? pair.second() : null;
+      Timer.Context time = stats.time(dir + "_peek");
+      try {
+        Pair<String, byte[]> pair = queue.peek();
+        return pair != null ? pair.second() : null;
+      } finally {
+        time.stop();
+      }
     }
 
     @Override
@@ -63,8 +79,18 @@ public class SimDistributedQueueFactory implements SolrCloudDataProvider.Distrib
 
     @Override
     public byte[] peek(long wait) throws Exception {
-      Pair<String, byte[]> pair = peekInternal(wait);
-      return pair != null ? pair.second() : null;
+      Timer.Context time;
+      if (wait == Long.MAX_VALUE) {
+        time = stats.time(dir + "_peek_wait_forever");
+      } else {
+        time = stats.time(dir + "_peek_wait" + wait);
+      }
+      try {
+        Pair<String, byte[]> pair = peekInternal(wait);
+        return pair != null ? pair.second() : null;
+      } finally {
+        time.stop();
+      }
     }
 
     private Pair<String, byte[]> peekInternal(long wait) throws Exception {
@@ -90,6 +116,7 @@ public class SimDistributedQueueFactory implements SolrCloudDataProvider.Distrib
 
     @Override
     public byte[] poll() throws Exception {
+      Timer.Context time = stats.time(dir + "_poll");
       updateLock.lockInterruptibly();
       try {
         Pair<String, byte[]>  pair = queue.poll();
@@ -101,11 +128,13 @@ public class SimDistributedQueueFactory implements SolrCloudDataProvider.Distrib
         }
       } finally {
         updateLock.unlock();
+        time.stop();
       }
     }
 
     @Override
     public byte[] remove() throws Exception {
+      Timer.Context time = stats.time(dir + "_remove");
       updateLock.lockInterruptibly();
       try {
         byte[] res = queue.remove().second();
@@ -113,11 +142,13 @@ public class SimDistributedQueueFactory implements SolrCloudDataProvider.Distrib
         return res;
       } finally {
         updateLock.unlock();
+        time.stop();
       }
     }
 
     @Override
     public byte[] take() throws Exception {
+      Timer.Context timer = stats.time(dir + "_take");
       updateLock.lockInterruptibly();
       try {
         while (true) {
@@ -129,11 +160,13 @@ public class SimDistributedQueueFactory implements SolrCloudDataProvider.Distrib
         }
       } finally {
         updateLock.unlock();
+        timer.stop();
       }
     }
 
     @Override
     public void offer(byte[] data) throws Exception {
+      Timer.Context time = stats.time(dir + "_offer");
       updateLock.lockInterruptibly();
       try {
         queue.offer(new Pair(String.format("qn-%010d", seq), data));
@@ -142,6 +175,7 @@ public class SimDistributedQueueFactory implements SolrCloudDataProvider.Distrib
         changed.signalAll();
       } finally {
         updateLock.unlock();
+        time.stop();
       }
     }
 
@@ -198,5 +232,34 @@ public class SimDistributedQueueFactory implements SolrCloudDataProvider.Distrib
         updateLock.unlock();
       }
     }
+
+    public Stats getZkStats() {
+      return stats;
+    }
+
+    @Override
+    public Map<String, Object> getStats() {
+      if (stats == null) {
+        return Collections.emptyMap();
+      }
+      Map<String, Object> res = new HashMap<>();
+      res.put("queueLength", stats.getQueueLength());
+      final Map<String, Object> statsMap = new HashMap<>();
+      res.put("stats", statsMap);
+      stats.getStats().forEach((op, stat) -> {
+        final Map<String, Object> statMap = new HashMap<>();
+        statMap.put("success", stat.success.get());
+        statMap.put("errors", stat.errors.get());
+        final List<Map<String, Object>> failed = new ArrayList<>(stat.failureDetails.size());
+        statMap.put("failureDetails", failed);
+        stat.failureDetails.forEach(failedOp -> {
+          Map<String, Object> fo = new HashMap<>();
+          fo.put("req", failedOp.req);
+          fo.put("resp", failedOp.resp);
+        });
+        statsMap.put(op, statMap);
+      });
+      return res;
+    }
   }
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/a578baee/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/SimNodeStateProvider.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/SimNodeStateProvider.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/SimNodeStateProvider.java
new file mode 100644
index 0000000..77d6056
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/SimNodeStateProvider.java
@@ -0,0 +1,69 @@
+package org.apache.solr.cloud.autoscaling.sim;
+
+import java.lang.invoke.MethodHandles;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.stream.Collectors;
+
+import org.apache.solr.client.solrj.cloud.autoscaling.NodeStateProvider;
+import org.apache.solr.client.solrj.cloud.autoscaling.ReplicaInfo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ *
+ */
+public class SimNodeStateProvider implements NodeStateProvider {
+  private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+  private final Map<String, Map<String, Object>> nodeValues = new ConcurrentHashMap<>();
+  private final SimClusterStateProvider stateProvider;
+
+  public SimNodeStateProvider(SimClusterStateProvider stateProvider, Map<String, Map<String, Object>> nodeValues) {
+    this.stateProvider = stateProvider;
+    if (nodeValues != null) {
+      this.nodeValues.putAll(nodeValues);
+    }
+  }
+
+  // -------- simulator setup methods ------------
+  public void simSetNodeValues(String node, Map<String, Object> values) {
+    nodeValues.put(node, new ConcurrentHashMap<>(values));
+  }
+
+  public void simSetNodeValue(String node, String key, Object value) {
+    nodeValues.computeIfAbsent(node, n -> new ConcurrentHashMap<>()).put(key, value);
+  }
+
+  // ---------- interface methods -------------
+
+  @Override
+  public Map<String, Object> getNodeValues(String node, Collection<String> tags) {
+    Map<String, Object> values = nodeValues.get(node);
+    if (values == null) {
+      return Collections.emptyMap();
+    }
+    return values.entrySet().stream().filter(e -> tags.contains(e.getKey())).collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue()));
+  }
+
+  @Override
+  public Map<String, Map<String, List<ReplicaInfo>>> getReplicaInfo(String node, Collection<String> keys) {
+    List<ReplicaInfo> replicas = stateProvider.simGetReplicaInfos(node);
+    if (replicas == null) {
+      return Collections.emptyMap();
+    }
+    Map<String, Map<String, List<ReplicaInfo>>> res = new HashMap<>();
+    for (ReplicaInfo r : replicas) {
+      Map<String, List<ReplicaInfo>> perCollection = res.computeIfAbsent(r.getCollection(), s -> new HashMap<>());
+      List<ReplicaInfo> perShard = perCollection.computeIfAbsent(r.getShard(), s -> new ArrayList<>());
+      perShard.add(r);
+    }
+    return res;
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/a578baee/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/TestClusterDataProvider.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/TestClusterDataProvider.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/TestClusterDataProvider.java
deleted file mode 100644
index 5485cc6..0000000
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/TestClusterDataProvider.java
+++ /dev/null
@@ -1,183 +0,0 @@
-package org.apache.solr.cloud.autoscaling.sim;
-
-import java.lang.invoke.MethodHandles;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-import org.apache.solr.client.solrj.cloud.autoscaling.AutoScalingConfig;
-import org.apache.solr.client.solrj.cloud.autoscaling.ClusterDataProvider;
-import org.apache.solr.client.solrj.cloud.autoscaling.Preference;
-import org.apache.solr.client.solrj.cloud.autoscaling.ReplicaInfo;
-import org.apache.solr.client.solrj.embedded.JettySolrRunner;
-import org.apache.solr.client.solrj.request.CollectionAdminRequest;
-import org.apache.solr.cloud.SolrCloudTestCase;
-import org.apache.solr.common.cloud.ClusterState;
-import org.apache.solr.common.cloud.DocCollection;
-import org.apache.solr.common.cloud.Replica;
-import org.apache.solr.common.cloud.Slice;
-import org.apache.solr.common.cloud.ZkStateReader;
-import org.apache.solr.common.cloud.rule.ImplicitSnitch;
-import org.apache.solr.common.params.CollectionAdminParams;
-import org.apache.solr.common.util.Utils;
-import org.apache.zookeeper.Watcher;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- *
- */
-public class TestClusterDataProvider extends SolrCloudTestCase {
-  private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
-
-  private static int NODE_COUNT = 3;
-  private static boolean simulated;
-
-  private static ClusterDataProvider clusterDataProvider;
-
-  private static Collection<String> liveNodes;
-  private static Map<String, Object> clusterProperties;
-  private static AutoScalingConfig autoScalingConfig;
-  private static Map<String, Map<String, Map<String, List<ReplicaInfo>>>> replicas;
-  private static Map<String, Map<String, Object>> nodeValues;
-  private static ClusterState realState;
-
-  // set up a real cluster as the source of test data
-  @BeforeClass
-  public static void setupCluster() throws Exception {
-    simulated = random().nextBoolean();
-    //simulated = true;
-
-    configureCluster(NODE_COUNT)
-        .addConfig("conf", configset("cloud-minimal"))
-        .configure();
-    CollectionAdminRequest.createCollection(CollectionAdminParams.SYSTEM_COLL, null, 1, 2, 0, 1)
-        .process(cluster.getSolrClient());
-    init();
-  }
-
-  private static void init() throws Exception {
-    ClusterDataProvider realProvider = cluster.getJettySolrRunner(cluster.getJettySolrRunners().size() - 1).getCoreContainer()
-        .getZkController().getSolrCloudDataProvider().getClusterDataProvider();
-    liveNodes = realProvider.getLiveNodes();
-    clusterProperties = realProvider.getClusterProperties();
-    autoScalingConfig = realProvider.getAutoScalingConfig();
-    replicas = new HashMap<>();
-    nodeValues = new HashMap<>();
-    realProvider.getLiveNodes().forEach(n -> {
-      replicas.put(n, realProvider.getReplicaInfo(n, Collections.emptySet()));
-      nodeValues.put(n, realProvider.getNodeValues(n, ImplicitSnitch.tags));
-    });
-    realState = realProvider.getClusterState();
-
-    if (simulated) {
-      // initialize simulated provider
-      SimClusterDataProvider simProvider = new SimClusterDataProvider();
-      simProvider.simSetAutoScalingConfig(autoScalingConfig);
-      nodeValues.forEach((n, values) -> {
-        simProvider.simSetNodeValues(n, values);
-      });
-      simProvider.simSetClusterState(realState);
-      ClusterState simState = simProvider.getClusterState();
-      assertClusterStateEquals(realState, simState);
-      // test the other constructor
-      ClusterDataProvider anotherProvider = new SimClusterDataProvider(realState, autoScalingConfig, nodeValues);
-      assertClusterStateEquals(realState, anotherProvider.getClusterState());
-      clusterDataProvider = simProvider;
-    } else {
-      clusterDataProvider = realProvider;
-    }
-  }
-
-  private static void assertClusterStateEquals(ClusterState one, ClusterState two) {
-    assertEquals(one.getLiveNodes(), two.getLiveNodes());
-    assertEquals(one.getCollectionsMap().keySet(), two.getCollectionsMap().keySet());
-    one.forEachCollection(oneColl -> {
-      DocCollection twoColl = two.getCollection(oneColl.getName());
-      Map<String, Slice> oneSlices = oneColl.getSlicesMap();
-      Map<String, Slice> twoSlices = twoColl.getSlicesMap();
-      assertEquals(oneSlices.keySet(), twoSlices.keySet());
-      oneSlices.forEach((s, slice) -> {
-        Slice sTwo = twoSlices.get(s);
-        for (Replica oneReplica : slice.getReplicas()) {
-          Replica twoReplica = sTwo.getReplica(oneReplica.getName());
-          assertNotNull(twoReplica);
-          assertEquals(oneReplica, twoReplica);
-        }
-      });
-    });
-  }
-
-  private String addNode() throws Exception {
-    JettySolrRunner solr = cluster.startJettySolrRunner();
-    String nodeId = solr.getNodeName();
-    if (simulated) {
-      ((SimClusterDataProvider)clusterDataProvider).simAddNode(nodeId);
-    }
-    return nodeId;
-  }
-
-  private String deleteNode() throws Exception {
-    String nodeId = cluster.getJettySolrRunner(0).getNodeName();
-    cluster.stopJettySolrRunner(0);
-    if (simulated) {
-      ((SimClusterDataProvider)clusterDataProvider).simRemoveNode(nodeId);
-    }
-    return nodeId;
-  }
-
-  private void setAutoScalingConfig(AutoScalingConfig cfg) throws Exception {
-    cluster.getJettySolrRunner(0).getCoreContainer().getZkController().getZkClient().setData(ZkStateReader.SOLR_AUTOSCALING_CONF_PATH,
-        Utils.toJSON(cfg), -1, true);
-    if (simulated) {
-      ((SimClusterDataProvider)clusterDataProvider).simSetAutoScalingConfig(cfg);
-    }
-  }
-
-  @Test
-  public void testAddRemoveNode() throws Exception {
-    Set<String> lastNodes = new HashSet<>(clusterDataProvider.getLiveNodes());
-    String node = addNode();
-    Thread.sleep(2000);
-    assertFalse(lastNodes.contains(node));
-    assertTrue(clusterDataProvider.getLiveNodes().contains(node));
-    node = deleteNode();
-    Thread.sleep(2000);
-    assertTrue(lastNodes.contains(node));
-    assertFalse(clusterDataProvider.getLiveNodes().contains(node));
-  }
-
-  @Test
-  public void testAutoScalingConfig() throws Exception {
-    final CountDownLatch triggered = new CountDownLatch(1);
-    Watcher w = ev -> {
-      if (triggered.getCount() == 0) {
-        fail("already triggered once!");
-      }
-      triggered.countDown();
-    };
-    AutoScalingConfig cfg = clusterDataProvider.getAutoScalingConfig(w);
-    assertEquals(autoScalingConfig, cfg);
-    Preference p = new Preference(Collections.singletonMap("maximize", "freedisk"));
-    cfg = cfg.withPolicy(cfg.getPolicy().withClusterPreferences(Collections.singletonList(p)));
-    setAutoScalingConfig(cfg);
-    if (!triggered.await(10, TimeUnit.SECONDS)) {
-      fail("Watch should be triggered on update!");
-    }
-    AutoScalingConfig cfg1 = clusterDataProvider.getAutoScalingConfig(null);
-    assertEquals(cfg, cfg1);
-
-    // restore
-    setAutoScalingConfig(autoScalingConfig);
-    cfg1 = clusterDataProvider.getAutoScalingConfig(null);
-    assertEquals(autoScalingConfig, cfg1);
-  }
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/a578baee/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/TestClusterStateProvider.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/TestClusterStateProvider.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/TestClusterStateProvider.java
new file mode 100644
index 0000000..795cf0d
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/TestClusterStateProvider.java
@@ -0,0 +1,181 @@
+package org.apache.solr.cloud.autoscaling.sim;
+
+import java.lang.invoke.MethodHandles;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.solr.client.solrj.cloud.autoscaling.AutoScalingConfig;
+import org.apache.solr.client.solrj.cloud.autoscaling.Preference;
+import org.apache.solr.client.solrj.cloud.autoscaling.ReplicaInfo;
+import org.apache.solr.client.solrj.cloud.autoscaling.SolrCloudManager;
+import org.apache.solr.client.solrj.embedded.JettySolrRunner;
+import org.apache.solr.client.solrj.request.CollectionAdminRequest;
+import org.apache.solr.cloud.SolrCloudTestCase;
+import org.apache.solr.common.cloud.ClusterState;
+import org.apache.solr.common.cloud.DocCollection;
+import org.apache.solr.common.cloud.Replica;
+import org.apache.solr.common.cloud.Slice;
+import org.apache.solr.common.cloud.ZkStateReader;
+import org.apache.solr.common.cloud.rule.ImplicitSnitch;
+import org.apache.solr.common.params.CollectionAdminParams;
+import org.apache.solr.common.util.Utils;
+import org.apache.zookeeper.Watcher;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ *
+ */
+public class TestClusterStateProvider extends SolrCloudTestCase {
+  private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+  private static int NODE_COUNT = 3;
+  private static boolean simulated;
+
+  private static SolrCloudManager cloudManager;
+
+  private static Collection<String> liveNodes;
+  private static Map<String, Object> clusterProperties;
+  private static AutoScalingConfig autoScalingConfig;
+  private static Map<String, Map<String, Map<String, List<ReplicaInfo>>>> replicas;
+  private static Map<String, Map<String, Object>> nodeValues;
+  private static ClusterState realState;
+
+  // set up a real cluster as the source of test data
+  @BeforeClass
+  public static void setupCluster() throws Exception {
+    simulated = random().nextBoolean();
+    //simulated = true;
+
+    configureCluster(NODE_COUNT)
+        .addConfig("conf", configset("cloud-minimal"))
+        .configure();
+    CollectionAdminRequest.createCollection(CollectionAdminParams.SYSTEM_COLL, null, 1, 2, 0, 1)
+        .process(cluster.getSolrClient());
+    init();
+  }
+
+  private static void init() throws Exception {
+    SolrCloudManager realManager = cluster.getJettySolrRunner(cluster.getJettySolrRunners().size() - 1).getCoreContainer()
+        .getZkController().getSolrCloudManager();
+    liveNodes = realManager.getClusterStateProvider().getLiveNodes();
+    clusterProperties = realManager.getClusterStateProvider().getClusterProperties();
+    autoScalingConfig = realManager.getDistribStateManager().getAutoScalingConfig();
+    replicas = new HashMap<>();
+    nodeValues = new HashMap<>();
+    liveNodes.forEach(n -> {
+      replicas.put(n, realManager.getNodeStateProvider().getReplicaInfo(n, Collections.emptySet()));
+      nodeValues.put(n, realManager.getNodeStateProvider().getNodeValues(n, ImplicitSnitch.tags));
+    });
+    realState = realManager.getClusterStateProvider().getClusterState();
+
+    if (simulated) {
+      // initialize simulated provider
+      SimCloudManager simCloudManager = new SimCloudManager();
+      simCloudManager.getSimClusterStateProvider().simSetClusterProperties(clusterProperties);
+      simCloudManager.getSimDistribStateManager().simSetAutoScalingConfig(autoScalingConfig);
+      nodeValues.forEach((n, values) -> {
+        simCloudManager.getSimNodeStateProvider().simSetNodeValues(n, values);
+      });
+      simCloudManager.getSimClusterStateProvider().simSetClusterState(realState);
+      ClusterState simState = simCloudManager.getClusterStateProvider().getClusterState();
+      assertClusterStateEquals(realState, simState);
+      cloudManager = simCloudManager;
+    } else {
+      cloudManager = realManager;
+    }
+  }
+
+  private static void assertClusterStateEquals(ClusterState one, ClusterState two) {
+    assertEquals(one.getLiveNodes(), two.getLiveNodes());
+    assertEquals(one.getCollectionsMap().keySet(), two.getCollectionsMap().keySet());
+    one.forEachCollection(oneColl -> {
+      DocCollection twoColl = two.getCollection(oneColl.getName());
+      Map<String, Slice> oneSlices = oneColl.getSlicesMap();
+      Map<String, Slice> twoSlices = twoColl.getSlicesMap();
+      assertEquals(oneSlices.keySet(), twoSlices.keySet());
+      oneSlices.forEach((s, slice) -> {
+        Slice sTwo = twoSlices.get(s);
+        for (Replica oneReplica : slice.getReplicas()) {
+          Replica twoReplica = sTwo.getReplica(oneReplica.getName());
+          assertNotNull(twoReplica);
+          assertEquals(oneReplica, twoReplica);
+        }
+      });
+    });
+  }
+
+  private String addNode() throws Exception {
+    JettySolrRunner solr = cluster.startJettySolrRunner();
+    String nodeId = solr.getNodeName();
+    if (simulated) {
+      ((SimCloudManager) cloudManager).getSimClusterStateProvider().simAddNode(nodeId);
+    }
+    return nodeId;
+  }
+
+  private String deleteNode() throws Exception {
+    String nodeId = cluster.getJettySolrRunner(0).getNodeName();
+    cluster.stopJettySolrRunner(0);
+    if (simulated) {
+      ((SimCloudManager) cloudManager).getSimClusterStateProvider().simRemoveNode(nodeId);
+    }
+    return nodeId;
+  }
+
+  private void setAutoScalingConfig(AutoScalingConfig cfg) throws Exception {
+    cluster.getJettySolrRunner(0).getCoreContainer().getZkController().getZkClient().setData(ZkStateReader.SOLR_AUTOSCALING_CONF_PATH,
+        Utils.toJSON(cfg), -1, true);
+    if (simulated) {
+      ((SimCloudManager) cloudManager).getSimDistribStateManager().simSetAutoScalingConfig(cfg);
+    }
+  }
+
+  @Test
+  public void testAddRemoveNode() throws Exception {
+    Set<String> lastNodes = new HashSet<>(cloudManager.getClusterStateProvider().getLiveNodes());
+    String node = addNode();
+    Thread.sleep(2000);
+    assertFalse(lastNodes.contains(node));
+    assertTrue(cloudManager.getClusterStateProvider().getLiveNodes().contains(node));
+    node = deleteNode();
+    Thread.sleep(2000);
+    assertTrue(lastNodes.contains(node));
+    assertFalse(cloudManager.getClusterStateProvider().getLiveNodes().contains(node));
+  }
+
+  @Test
+  public void testAutoScalingConfig() throws Exception {
+    final CountDownLatch triggered = new CountDownLatch(1);
+    Watcher w = ev -> {
+      if (triggered.getCount() == 0) {
+        fail("already triggered once!");
+      }
+      triggered.countDown();
+    };
+    AutoScalingConfig cfg = cloudManager.getDistribStateManager().getAutoScalingConfig(w);
+    assertEquals(autoScalingConfig, cfg);
+    Preference p = new Preference(Collections.singletonMap("maximize", "freedisk"));
+    cfg = cfg.withPolicy(cfg.getPolicy().withClusterPreferences(Collections.singletonList(p)));
+    setAutoScalingConfig(cfg);
+    if (!triggered.await(10, TimeUnit.SECONDS)) {
+      fail("Watch should be triggered on update!");
+    }
+    AutoScalingConfig cfg1 = cloudManager.getDistribStateManager().getAutoScalingConfig(null);
+    assertEquals(cfg, cfg1);
+
+    // restore
+    setAutoScalingConfig(autoScalingConfig);
+    cfg1 = cloudManager.getDistribStateManager().getAutoScalingConfig(null);
+    assertEquals(autoScalingConfig, cfg1);
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/a578baee/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/TestDistribStateManager.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/TestDistribStateManager.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/TestDistribStateManager.java
index 7019769..04a781b 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/TestDistribStateManager.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/TestDistribStateManager.java
@@ -27,6 +27,7 @@ import java.util.concurrent.TimeUnit;
 
 import org.apache.solr.SolrTestCaseJ4;
 import org.apache.solr.client.solrj.cloud.autoscaling.DistribStateManager;
+import org.apache.solr.client.solrj.cloud.autoscaling.VersionedData;
 import org.apache.solr.client.solrj.impl.ZkDistribStateManager;
 import org.apache.solr.cloud.ZkTestServer;
 import org.apache.solr.common.cloud.SolrZkClient;
@@ -223,10 +224,10 @@ public class TestDistribStateManager extends SolrTestCaseJ4 {
     stateManager.makePath("/getData");
     stateManager.createData("/getData/persistentData", firstData, CreateMode.PERSISTENT);
     OnceWatcher nodeWatcher = new OnceWatcher();
-    DistribStateManager.VersionedData vd = stateManager.getData("/getData/persistentData", nodeWatcher);
+    VersionedData vd = stateManager.getData("/getData/persistentData", nodeWatcher);
     assertNotNull(vd);
-    assertEquals(0, vd.version);
-    assertTrue(Arrays.equals(firstData, vd.data));
+    assertEquals(0, vd.getVersion());
+    assertTrue(Arrays.equals(firstData, vd.getData()));
 
     // update data, test versioning
     try {
@@ -259,7 +260,7 @@ public class TestDistribStateManager extends SolrTestCaseJ4 {
     vd = stateManager.getData("/getData/persistentData", nodeWatcher);
     // try wrong version
     try {
-      stateManager.removeData("/getData/persistentData", vd.version - 1);
+      stateManager.removeData("/getData/persistentData", vd.getVersion() - 1);
       fail("should have failed");
     } catch (IOException e) {
       // expected
@@ -267,7 +268,7 @@ public class TestDistribStateManager extends SolrTestCaseJ4 {
     // watch should not have fired
     assertEquals(1, nodeWatcher.triggered.getCount());
 
-    stateManager.removeData("/getData/persistentData", vd.version);
+    stateManager.removeData("/getData/persistentData", vd.getVersion());
     if (!nodeWatcher.triggered.await(5, TimeUnit.SECONDS)) {
       fail("Node watch should have fired!");
     }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/a578baee/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/TestSimClusterDataProvider.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/TestSimClusterDataProvider.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/TestSimClusterDataProvider.java
deleted file mode 100644
index fb21c40..0000000
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/TestSimClusterDataProvider.java
+++ /dev/null
@@ -1,77 +0,0 @@
-package org.apache.solr.cloud.autoscaling.sim;
-
-import java.util.Set;
-
-import com.google.common.collect.ImmutableSet;
-import org.apache.solr.SolrTestCaseJ4;
-import org.apache.solr.client.solrj.request.CollectionAdminRequest;
-import org.apache.solr.common.cloud.ClusterState;
-import org.apache.solr.common.cloud.Replica;
-import org.junit.Before;
-import org.junit.Test;
-
-import static java.nio.charset.StandardCharsets.UTF_8;
-
-/**
- *
- */
-public class TestSimClusterDataProvider extends SolrTestCaseJ4 {
-
-  private SimClusterDataProvider dataProvider;
-
-  private static final String COLL = "gettingstarted";
-  private static final Set<String> initialLiveNodes = ImmutableSet.of("192.168.1.108:7574_solr", "192.168.1.108:8983_solr");
-
-  private String coll1State = "{'gettingstarted':{\n" +
-      "    'replicationFactor':'2',\n" +
-      "    'router':{'name':'compositeId'},\n" +
-      "    'maxShardsPerNode':'2',\n" +
-      "    'autoAddReplicas':'false',\n" +
-      "    'shards':{\n" +
-      "      'shard1':{\n" +
-      "        'range':'80000000-ffffffff',\n" +
-      "        'state':'active',\n" +
-      "        'replicas':{\n" +
-      "          'core_node2':{\n" +
-      "            'core':'gettingstarted_shard1_replica1',\n" +
-      "            'base_url':'http://192.168.1.108:8983/solr',\n" +
-      "            'node_name':'192.168.1.108:8983_solr',\n" +
-      "            'state':'active',\n" +
-      "            'leader':'true'},\n" +
-      "          'core_node4':{\n" +
-      "            'core':'gettingstarted_shard1_replica2',\n" +
-      "            'base_url':'http://192.168.1.108:7574/solr',\n" +
-      "            'node_name':'192.168.1.108:7574_solr',\n" +
-      "            'state':'active'}}},\n" +
-      "      'shard2':{\n" +
-      "        'range':'0-7fffffff',\n" +
-      "        'state':'active',\n" +
-      "        'replicas':{\n" +
-      "          'core_node1':{\n" +
-      "            'core':'gettingstarted_shard2_replica1',\n" +
-      "            'base_url':'http://192.168.1.108:8983/solr',\n" +
-      "            'node_name':'192.168.1.108:8983_solr',\n" +
-      "            'state':'active',\n" +
-      "            'leader':'true'},\n" +
-      "          'core_node3':{\n" +
-      "            'core':'gettingstarted_shard2_replica2',\n" +
-      "            'base_url':'http://192.168.1.108:7574/solr',\n" +
-      "            'node_name':'192.168.1.108:7574_solr',\n" +
-      "            'state':'active'}}}}}}";
-
-
-  @Before
-  public void setup() throws Exception {
-    ClusterState cs = ClusterState.load(1, coll1State.getBytes(UTF_8),
-        initialLiveNodes, "/collections/gettingstarted/state.json");
-    dataProvider = new SimClusterDataProvider(cs, null, null);
-  }
-
-  @Test
-  public void testAddReplica() throws Exception {
-
-    CollectionAdminRequest req = CollectionAdminRequest.addReplicaToShard(COLL, "shard1", Replica.Type.PULL);
-    dataProvider.simHandleSolrRequest(req);
-  }
-
-}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/a578baee/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/TestSimClusterStateProvider.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/TestSimClusterStateProvider.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/TestSimClusterStateProvider.java
new file mode 100644
index 0000000..decb1b3
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/TestSimClusterStateProvider.java
@@ -0,0 +1,78 @@
+package org.apache.solr.cloud.autoscaling.sim;
+
+import java.util.Set;
+
+import com.google.common.collect.ImmutableSet;
+import org.apache.solr.SolrTestCaseJ4;
+import org.apache.solr.client.solrj.request.CollectionAdminRequest;
+import org.apache.solr.common.cloud.ClusterState;
+import org.apache.solr.common.cloud.Replica;
+import org.junit.Before;
+import org.junit.Test;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+/**
+ *
+ */
+public class TestSimClusterStateProvider extends SolrTestCaseJ4 {
+
+  private SimClusterStateProvider dataProvider;
+
+  private static final String COLL = "gettingstarted";
+  private static final Set<String> initialLiveNodes = ImmutableSet.of("192.168.1.108:7574_solr", "192.168.1.108:8983_solr");
+
+  private String coll1State = "{'gettingstarted':{\n" +
+      "    'replicationFactor':'2',\n" +
+      "    'router':{'name':'compositeId'},\n" +
+      "    'maxShardsPerNode':'2',\n" +
+      "    'autoAddReplicas':'false',\n" +
+      "    'shards':{\n" +
+      "      'shard1':{\n" +
+      "        'range':'80000000-ffffffff',\n" +
+      "        'state':'active',\n" +
+      "        'replicas':{\n" +
+      "          'core_node2':{\n" +
+      "            'core':'gettingstarted_shard1_replica1',\n" +
+      "            'base_url':'http://192.168.1.108:8983/solr',\n" +
+      "            'node_name':'192.168.1.108:8983_solr',\n" +
+      "            'state':'active',\n" +
+      "            'leader':'true'},\n" +
+      "          'core_node4':{\n" +
+      "            'core':'gettingstarted_shard1_replica2',\n" +
+      "            'base_url':'http://192.168.1.108:7574/solr',\n" +
+      "            'node_name':'192.168.1.108:7574_solr',\n" +
+      "            'state':'active'}}},\n" +
+      "      'shard2':{\n" +
+      "        'range':'0-7fffffff',\n" +
+      "        'state':'active',\n" +
+      "        'replicas':{\n" +
+      "          'core_node1':{\n" +
+      "            'core':'gettingstarted_shard2_replica1',\n" +
+      "            'base_url':'http://192.168.1.108:8983/solr',\n" +
+      "            'node_name':'192.168.1.108:8983_solr',\n" +
+      "            'state':'active',\n" +
+      "            'leader':'true'},\n" +
+      "          'core_node3':{\n" +
+      "            'core':'gettingstarted_shard2_replica2',\n" +
+      "            'base_url':'http://192.168.1.108:7574/solr',\n" +
+      "            'node_name':'192.168.1.108:7574_solr',\n" +
+      "            'state':'active'}}}}}}";
+
+
+  @Before
+  public void setup() throws Exception {
+    ClusterState cs = ClusterState.load(1, coll1State.getBytes(UTF_8),
+        initialLiveNodes, "/collections/gettingstarted/state.json");
+    dataProvider = new SimClusterStateProvider();
+    dataProvider.simSetClusterState(cs);
+  }
+
+  @Test
+  public void testAddReplica() throws Exception {
+
+    CollectionAdminRequest req = CollectionAdminRequest.addReplicaToShard(COLL, "shard1", Replica.Type.PULL);
+    dataProvider.simHandleSolrRequest(req);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/a578baee/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/TestSimDistributedQueue.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/TestSimDistributedQueue.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/TestSimDistributedQueue.java
index cbfe553..4d65910 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/TestSimDistributedQueue.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/sim/TestSimDistributedQueue.java
@@ -166,7 +166,7 @@ public class TestSimDistributedQueue extends SolrTestCaseJ4 {
 
 
   protected DistributedQueue makeDistributedQueue(String dqZNode) throws Exception {
-    return new SimDistributedQueueFactory.SimDistributedQueue();
+    return new SimDistributedQueueFactory.SimDistributedQueue(dqZNode);
   }
 
   private static class QueueChangerThread extends Thread {