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 2020/07/16 15:43:09 UTC

[lucene-solr] 01/01: SOLR-14613: Initial POC based on Ilan's proposal.

This is an automated email from the ASF dual-hosted git repository.

ab pushed a commit to branch jira/solr-14613
in repository https://gitbox.apache.org/repos/asf/lucene-solr.git

commit 5f6887de5d5977cb8629ff28147b485291b12a05
Author: Andrzej Bialecki <ab...@apache.org>
AuthorDate: Thu Jul 16 17:42:27 2020 +0200

    SOLR-14613: Initial POC based on Ilan's proposal.
---
 .../api/collections/assign/AddReplicaDecision.java |  34 +++++
 .../api/collections/assign/AddReplicaRequest.java  |  72 ++++++++++
 .../api/collections/assign/AssignDecision.java     |  26 ++++
 .../api/collections/assign/AssignDecisions.java    |  41 ++++++
 .../api/collections/assign/AssignRequest.java      |  27 ++++
 .../cloud/api/collections/assign/Assigner.java     |  30 ++++
 .../collections/assign/AssignerClusterState.java   |  44 ++++++
 .../assign/AssignerCollectionState.java            |  34 +++++
 .../api/collections/assign/AssignerException.java  |  18 +++
 .../api/collections/assign/AssignerNodeState.java  |  34 +++++
 .../api/collections/assign/AssignerReplica.java    |  86 +++++++++++
 .../api/collections/assign/AssignerShardState.java |  31 ++++
 .../api/collections/assign/BaseAssignDecision.java |  18 +++
 .../api/collections/assign/BaseAssignRequest.java  |  45 ++++++
 .../collections/assign/DeleteReplicaDecision.java  |  26 ++++
 .../collections/assign/DeleteReplicaRequest.java   |  33 +++++
 .../collections/assign/MoveReplicaDecision.java    |  36 +++++
 .../api/collections/assign/MoveReplicaRequest.java |  28 ++++
 .../cloud/api/collections/assign/NoopDecision.java |  21 +++
 .../assign/policy8x/AssignerCloudManager.java      |  81 +++++++++++
 .../policy8x/AssignerClusterStateProvider.java     | 102 +++++++++++++
 .../policy8x/AssignerDistribStateManager.java      |  99 +++++++++++++
 .../assign/policy8x/AssignerNodeStateProvider.java |  56 ++++++++
 .../assign/policy8x/Policy8xAssigner.java          |  61 ++++++++
 .../SolrCloudManagerAssignerClusterState.java      | 159 +++++++++++++++++++++
 .../collections/assign/policy8x/TestPolicy8x.java  |  44 ++++++
 26 files changed, 1286 insertions(+)

diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/AddReplicaDecision.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/AddReplicaDecision.java
new file mode 100644
index 0000000..a5e29e2
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/AddReplicaDecision.java
@@ -0,0 +1,34 @@
+/*
+ * 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.api.collections.assign;
+
+/**
+ *
+ */
+public class AddReplicaDecision extends BaseAssignDecision {
+  private final String targetNode;
+
+  public AddReplicaDecision(AssignRequest request, String targetNode) {
+    super(request);
+    this.targetNode = targetNode;
+  }
+
+  public String getTargetNode() {
+    return targetNode;
+  }
+}
diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/AddReplicaRequest.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/AddReplicaRequest.java
new file mode 100644
index 0000000..c4d9b38
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/AddReplicaRequest.java
@@ -0,0 +1,72 @@
+/*
+ * 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.api.collections.assign;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+import org.apache.solr.common.cloud.Replica;
+
+/**
+ *
+ */
+public class AddReplicaRequest extends BaseAssignRequest {
+  private final Replica.Type type;
+  private final Map<String, Object> params = new HashMap<>();
+  private final String targetNode;
+  private final String coreName;
+  private final Set<String> nodeSet;
+
+  public AddReplicaRequest(String collection, String shard, Replica.Type type, Map<String, Object> params,
+                           String targetNode, String coreName, Set<String> nodeSet) {
+    super(collection, shard);
+    this.type = type;
+    if (params != null) {
+      this.params.putAll(params);
+    }
+    this.targetNode = targetNode;
+    this.coreName = coreName;
+    this.nodeSet = nodeSet;
+    Objects.requireNonNull(this.type, "'type' must not be null");
+    Objects.requireNonNull(this.coreName, "'coreName' must not be null");
+  }
+
+  public Replica.Type getType() {
+    return type;
+  }
+
+  public Map<String, Object> getParams() {
+    return params;
+  }
+
+  // impls may request a specific target node
+  public String getTargetNode() {
+    return targetNode;
+  }
+
+  public String getCoreName() {
+    return coreName;
+  }
+
+  // subset of live nodes to consider as valid targets, or null
+  public Set<String> getNodeSet() {
+    return nodeSet;
+  }
+}
diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/AssignDecision.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/AssignDecision.java
new file mode 100644
index 0000000..9e0cfaa
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/AssignDecision.java
@@ -0,0 +1,26 @@
+/*
+ * 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.api.collections.assign;
+
+/**
+ *
+ */
+public interface AssignDecision {
+
+  AssignRequest getAssignRequest();
+
+}
diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/AssignDecisions.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/AssignDecisions.java
new file mode 100644
index 0000000..9626a55
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/AssignDecisions.java
@@ -0,0 +1,41 @@
+/*
+ * 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.api.collections.assign;
+
+import java.util.Collection;
+
+/**
+ *
+ */
+public class AssignDecisions {
+
+  private final AssignerClusterState outputClusterState;
+  private final Collection<AssignDecision> decisions;
+
+  public AssignDecisions(AssignerClusterState outputClusterState, Collection<AssignDecision> decisions) {
+    this.outputClusterState = outputClusterState;
+    this.decisions = decisions;
+  }
+
+  AssignerClusterState getOutputClusterState() {
+    return outputClusterState;
+  }
+
+  Collection<AssignDecision> getAssignmentDecisions() {
+    return decisions;
+  }
+}
diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/AssignRequest.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/AssignRequest.java
new file mode 100644
index 0000000..3d7ab0f
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/AssignRequest.java
@@ -0,0 +1,27 @@
+/*
+ * 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.api.collections.assign;
+
+/**
+ *
+ */
+public interface AssignRequest {
+
+  String getCollection();
+
+  String getShard();
+}
diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/Assigner.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/Assigner.java
new file mode 100644
index 0000000..3388500
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/Assigner.java
@@ -0,0 +1,30 @@
+/*
+ * 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.api.collections.assign;
+
+import java.util.List;
+
+/**
+ * Interface implemented by assign strategies.
+ */
+public interface Assigner {
+
+  AssignDecisions computeAssignments(AssignerClusterState initialState,
+                                     List<AssignRequest> requests) throws InterruptedException, AssignerException;
+
+}
\ No newline at end of file
diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/AssignerClusterState.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/AssignerClusterState.java
new file mode 100644
index 0000000..5deaa01
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/AssignerClusterState.java
@@ -0,0 +1,44 @@
+/*
+ * 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.api.collections.assign;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Initial cluster state for assigner.
+ */
+public interface AssignerClusterState {
+  Set<String> getNodes();
+  Set<String> getLiveNodes();
+  Collection<String> getCollections();
+
+  default AssignerNodeState getNodeState(String nodeName) {
+    return getNodeState(nodeName, Collections.emptySet(), Collections.emptySet());
+  }
+
+  AssignerNodeState getNodeState(String nodeName, Collection<String> nodeKeys, Collection<String> replicaKeys);
+
+  AssignerCollectionState getCollectionState(String collectionName, Collection<String> replicaKeys);
+
+  // other cluster properties
+  Map<String, Object> getProperties();
+
+}
diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/AssignerCollectionState.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/AssignerCollectionState.java
new file mode 100644
index 0000000..f848345
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/AssignerCollectionState.java
@@ -0,0 +1,34 @@
+/*
+ * 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.api.collections.assign;
+
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ *
+ */
+public interface AssignerCollectionState {
+
+  String getCollection();
+  Collection<String> getShards();
+  AssignerShardState getShardState(String shardName);
+
+  // other collection properties (router, policy settings, etc)
+  Map<String, Object> getProperties();
+}
diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/AssignerException.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/AssignerException.java
new file mode 100644
index 0000000..d64ca34
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/AssignerException.java
@@ -0,0 +1,18 @@
+package org.apache.solr.cloud.api.collections.assign;
+
+/**
+ *
+ */
+public class AssignerException extends Exception {
+  public AssignerException(String message) {
+    super(message);
+  }
+
+  public AssignerException(String message, Throwable cause) {
+    super(message, cause);
+  }
+
+  public AssignerException(Throwable cause) {
+    super(cause);
+  }
+}
diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/AssignerNodeState.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/AssignerNodeState.java
new file mode 100644
index 0000000..e6bffe1
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/AssignerNodeState.java
@@ -0,0 +1,34 @@
+/*
+ * 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.api.collections.assign;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ *
+ */
+public interface AssignerNodeState {
+  Collection<AssignerReplica> getReplicas();
+
+  long getTotalDiskGB();
+
+  long getFreeDiskGB();
+
+  Map<String, Object> getProperties();
+}
diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/AssignerReplica.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/AssignerReplica.java
new file mode 100644
index 0000000..192ae0e
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/AssignerReplica.java
@@ -0,0 +1,86 @@
+/*
+ * 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.api.collections.assign;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.solr.common.cloud.Replica;
+
+/**
+ * Represents basic information about a replica.
+ */
+public class AssignerReplica {
+  private final String name;
+  private final String node;
+  private final String collection;
+  private final String shard;
+  private final String core;
+  private final Replica.Type type;
+  private final Replica.State state;
+  private final Map<String, Object> properties = new HashMap<>();
+
+  public AssignerReplica(String name, String node,
+                         String collection, String shard, String core,
+                         Replica.Type type, Replica.State state,
+                         Map<String, Object> properties) {
+    this.name = name;
+    this.node = node;
+    this.collection = collection;
+    this.shard = shard;
+    this.core = core;
+    this.type = type;
+    this.state = state;
+    if (properties != null) {
+      this.properties.putAll(properties);
+    }
+  }
+
+  // so-called coreNode name
+  public String getName() {
+    return name;
+  }
+
+  public String getNode() {
+    return node;
+  }
+
+  public String getCollection() {
+    return collection;
+  }
+
+  public String getShard() {
+    return shard;
+  }
+
+  // SolrCore name
+  public String getCore() {
+    return core;
+  }
+
+  public Replica.Type getType() {
+    return type;
+  }
+
+  public Replica.State getState() {
+    return state;
+  }
+
+  public Map<String, Object> getProperties() {
+    return properties;
+  }
+}
diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/AssignerShardState.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/AssignerShardState.java
new file mode 100644
index 0000000..93c83b0
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/AssignerShardState.java
@@ -0,0 +1,31 @@
+/*
+ * 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.api.collections.assign;
+
+import java.util.Collection;
+import java.util.Map;
+
+/**
+ *
+ */
+public interface AssignerShardState {
+  String getName();
+
+  Collection<AssignerReplica> getReplicas();
+
+  Map<String, Object> getProperties();
+}
diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/BaseAssignDecision.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/BaseAssignDecision.java
new file mode 100644
index 0000000..c045997
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/BaseAssignDecision.java
@@ -0,0 +1,18 @@
+package org.apache.solr.cloud.api.collections.assign;
+
+/**
+ *
+ */
+public abstract class BaseAssignDecision implements AssignDecision {
+
+  protected final AssignRequest request;
+
+  protected BaseAssignDecision(AssignRequest request) {
+    this.request = request;
+  }
+
+  @Override
+  public AssignRequest getAssignRequest() {
+    return request;
+  }
+}
diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/BaseAssignRequest.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/BaseAssignRequest.java
new file mode 100644
index 0000000..dda8d65
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/BaseAssignRequest.java
@@ -0,0 +1,45 @@
+/*
+ * 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.api.collections.assign;
+
+import java.util.Objects;
+
+/**
+ *
+ */
+public abstract class BaseAssignRequest implements AssignRequest {
+
+  protected final String collection;
+  protected final String shard;
+
+  protected BaseAssignRequest(String collection, String shard) {
+    this.collection = collection;
+    this.shard = shard;
+    Objects.requireNonNull(this.collection, "'collection' must not be null");
+    Objects.requireNonNull(this.shard, "'shard' must not be null");
+  }
+
+  @Override
+  public String getCollection() {
+    return collection;
+  }
+
+  @Override
+  public String getShard() {
+    return shard;
+  }
+}
diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/DeleteReplicaDecision.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/DeleteReplicaDecision.java
new file mode 100644
index 0000000..1fae136
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/DeleteReplicaDecision.java
@@ -0,0 +1,26 @@
+/*
+ * 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.api.collections.assign;
+
+/**
+ *
+ */
+public class DeleteReplicaDecision extends BaseAssignDecision {
+  public DeleteReplicaDecision(AssignRequest request) {
+    super(request);
+  }
+}
diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/DeleteReplicaRequest.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/DeleteReplicaRequest.java
new file mode 100644
index 0000000..3c75926
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/DeleteReplicaRequest.java
@@ -0,0 +1,33 @@
+/*
+ * 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.api.collections.assign;
+
+/**
+ *
+ */
+public class DeleteReplicaRequest extends BaseAssignRequest {
+  private final String replicaName;
+
+  public DeleteReplicaRequest(String collection, String shard, String replicaName) {
+    super(collection, shard);
+    this.replicaName = replicaName;
+  }
+
+  public String getReplicaName() {
+    return replicaName;
+  }
+}
diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/MoveReplicaDecision.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/MoveReplicaDecision.java
new file mode 100644
index 0000000..db3ee80
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/MoveReplicaDecision.java
@@ -0,0 +1,36 @@
+/*
+ * 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.api.collections.assign;
+
+import org.apache.solr.common.cloud.Replica;
+
+/**
+ *
+ */
+public interface MoveReplicaDecision extends AssignDecision {
+  String getSourceNodeName();
+
+  String getTargetNodeName();
+
+  String getCollectionName();
+
+  String getSliceName();
+
+  String getReplicaName();
+
+  Replica.Type getReplicaType();
+}
diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/MoveReplicaRequest.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/MoveReplicaRequest.java
new file mode 100644
index 0000000..32df4bc
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/MoveReplicaRequest.java
@@ -0,0 +1,28 @@
+/*
+ * 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.api.collections.assign;
+
+/**
+ *
+ */
+public interface MoveReplicaRequest extends AssignRequest {
+  String getSourceNode();
+
+  String getReplicaName();
+
+  String getTargetNode();
+}
diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/NoopDecision.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/NoopDecision.java
new file mode 100644
index 0000000..4fe90e1
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/NoopDecision.java
@@ -0,0 +1,21 @@
+package org.apache.solr.cloud.api.collections.assign;
+
+/**
+ *
+ */
+public class NoopDecision extends BaseAssignDecision {
+  private final String reason;
+
+  public NoopDecision(String reason, AssignRequest request) {
+    super(request);
+    this.reason = reason;
+  }
+
+  public String getReason() {
+    return reason;
+  }
+
+  public static NoopDecision of(String reason, AssignRequest request) {
+    return new NoopDecision(reason, request);
+  }
+}
diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/policy8x/AssignerCloudManager.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/policy8x/AssignerCloudManager.java
new file mode 100644
index 0000000..8476ff2
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/policy8x/AssignerCloudManager.java
@@ -0,0 +1,81 @@
+package org.apache.solr.cloud.api.collections.assign.policy8x;
+
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.solr.client.solrj.SolrRequest;
+import org.apache.solr.client.solrj.SolrResponse;
+import org.apache.solr.client.solrj.cloud.DistribStateManager;
+import org.apache.solr.client.solrj.cloud.DistributedQueueFactory;
+import org.apache.solr.client.solrj.cloud.NodeStateProvider;
+import org.apache.solr.client.solrj.cloud.SolrCloudManager;
+import org.apache.solr.client.solrj.impl.ClusterStateProvider;
+import org.apache.solr.cloud.api.collections.assign.AssignerClusterState;
+import org.apache.solr.common.util.ObjectCache;
+import org.apache.solr.common.util.TimeSource;
+
+/**
+ *
+ */
+public class AssignerCloudManager implements SolrCloudManager {
+  private final ObjectCache objectCache = new ObjectCache();
+  private final TimeSource timeSource;
+  private final AssignerClusterState assignerClusterState;
+  private final AssignerClusterStateProvider clusterStateProvider;
+  private final AssignerNodeStateProvider nodeStateProvider;
+  private final AssignerDistribStateManager distribStateManager;
+
+  public AssignerCloudManager(AssignerClusterState assignerClusterState,
+                              TimeSource timeSource) {
+    this.assignerClusterState = assignerClusterState;
+    this.timeSource = timeSource;
+    clusterStateProvider = new AssignerClusterStateProvider(assignerClusterState);
+    nodeStateProvider = new AssignerNodeStateProvider(assignerClusterState);
+    distribStateManager = new AssignerDistribStateManager(assignerClusterState);
+  }
+
+  @Override
+  public ClusterStateProvider getClusterStateProvider() {
+    return clusterStateProvider;
+  }
+
+  @Override
+  public NodeStateProvider getNodeStateProvider() {
+    return nodeStateProvider;
+  }
+
+  @Override
+  public DistribStateManager getDistribStateManager() {
+    return distribStateManager;
+  }
+
+  @Override
+  public DistributedQueueFactory getDistributedQueueFactory() {
+    throw new UnsupportedOperationException("getDistributedQueueFactory");
+  }
+
+  @Override
+  public ObjectCache getObjectCache() {
+    return objectCache;
+  }
+
+  @Override
+  public TimeSource getTimeSource() {
+    return timeSource;
+  }
+
+  @Override
+  public SolrResponse request(SolrRequest req) throws IOException {
+    throw new UnsupportedOperationException("request");
+  }
+
+  @Override
+  public byte[] httpRequest(String url, SolrRequest.METHOD method, Map<String, String> headers, String payload, int timeout, boolean followRedirects) throws IOException {
+    throw new UnsupportedOperationException("httpRequest");
+  }
+
+  @Override
+  public void close() throws IOException {
+
+  }
+}
diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/policy8x/AssignerClusterStateProvider.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/policy8x/AssignerClusterStateProvider.java
new file mode 100644
index 0000000..fdb7278
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/policy8x/AssignerClusterStateProvider.java
@@ -0,0 +1,102 @@
+package org.apache.solr.cloud.api.collections.assign.policy8x;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.solr.client.solrj.impl.ClusterStateProvider;
+import org.apache.solr.cloud.api.collections.assign.AssignerClusterState;
+import org.apache.solr.cloud.api.collections.assign.AssignerCollectionState;
+import org.apache.solr.cloud.api.collections.assign.AssignerShardState;
+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;
+
+/**
+ *
+ */
+public class AssignerClusterStateProvider implements ClusterStateProvider {
+
+  private final AssignerClusterState state;
+  private final ClusterState clusterState;
+
+  public AssignerClusterStateProvider(AssignerClusterState state) {
+    this.state = state;
+    // build ClusterState
+    Map<String, DocCollection> collections = new HashMap<>();
+    state.getCollections().forEach(coll -> {
+      AssignerCollectionState collState = state.getCollectionState(coll, Collections.emptySet());
+      Map<String, Slice> slices = new HashMap<>();
+      collState.getShards().forEach(shard -> {
+        AssignerShardState shardState = collState.getShardState(shard);
+        Map<String, Replica> replicas = new HashMap<>();
+        shardState.getReplicas().forEach(ar -> {
+          Replica r = new Replica(ar.getName(), ar.getNode(), ar.getCollection(), ar.getShard(),
+              ar.getCore(), ar.getState(), ar.getType(), ar.getProperties());
+          replicas.put(r.getName(), r);
+        });
+        Slice slice = new Slice(shard, replicas, shardState.getProperties(), coll);
+        slices.put(slice.getName(), slice);
+      });
+      Map<String, Object> routerProp = (Map<String, Object>) collState.getProperties().getOrDefault(DocCollection.DOC_ROUTER, Collections.singletonMap("name", DocRouter.DEFAULT_NAME));
+      DocRouter router = DocRouter.getDocRouter((String)routerProp.getOrDefault("name", DocRouter.DEFAULT_NAME));
+      DocCollection docCollection = new DocCollection(coll, slices, collState.getProperties(), router, -1);
+      collections.put(docCollection.getName(), docCollection);
+    });
+    clusterState = new ClusterState(state.getLiveNodes(), collections);
+  }
+
+  @Override
+  public ClusterState.CollectionRef getState(String collection) {
+    return null;
+  }
+
+  @Override
+  public Set<String> getLiveNodes() {
+    return state.getLiveNodes();
+  }
+
+  @Override
+  public List<String> resolveAlias(String alias) {
+    throw new UnsupportedOperationException("resolveAlias");
+  }
+
+  @Override
+  public Map<String, String> getAliasProperties(String alias) {
+    throw new UnsupportedOperationException("getAliasProperties");
+  }
+
+  @Override
+  public ClusterState getClusterState() throws IOException {
+    return clusterState;
+  }
+
+  @Override
+  public Map<String, Object> getClusterProperties() {
+    return state.getProperties();
+  }
+
+  @Override
+  public String getPolicyNameByCollection(String coll) {
+    DocCollection docCollection = clusterState.getCollectionOrNull(coll);
+    if (docCollection == null) {
+      return null;
+    }
+    return docCollection.getPolicyName();
+  }
+
+  @Override
+  public void connect() {
+
+  }
+
+  @Override
+  public void close() throws IOException {
+
+  }
+}
diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/policy8x/AssignerDistribStateManager.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/policy8x/AssignerDistribStateManager.java
new file mode 100644
index 0000000..c8115cf
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/policy8x/AssignerDistribStateManager.java
@@ -0,0 +1,99 @@
+package org.apache.solr.cloud.api.collections.assign.policy8x;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+import org.apache.solr.client.solrj.cloud.DistribStateManager;
+import org.apache.solr.client.solrj.cloud.autoscaling.AlreadyExistsException;
+import org.apache.solr.client.solrj.cloud.autoscaling.AutoScalingConfig;
+import org.apache.solr.client.solrj.cloud.autoscaling.BadVersionException;
+import org.apache.solr.client.solrj.cloud.autoscaling.NotEmptyException;
+import org.apache.solr.client.solrj.cloud.autoscaling.VersionedData;
+import org.apache.solr.cloud.api.collections.assign.AssignerClusterState;
+import org.apache.solr.common.cloud.ZkStateReader;
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.Op;
+import org.apache.zookeeper.OpResult;
+import org.apache.zookeeper.Watcher;
+
+/**
+ *
+ */
+public class AssignerDistribStateManager implements DistribStateManager {
+
+  private final AutoScalingConfig autoScalingConfig;
+
+  public AssignerDistribStateManager(AssignerClusterState assignerClusterState) {
+    String autoscalingJson = (String) assignerClusterState.getProperties().get(ZkStateReader.SOLR_AUTOSCALING_CONF_PATH);
+    if (autoscalingJson != null) {
+      autoScalingConfig = new AutoScalingConfig(autoscalingJson.getBytes(StandardCharsets.UTF_8));
+    } else {
+      autoScalingConfig = new AutoScalingConfig(Collections.emptyMap());
+    }
+  }
+
+  @Override
+  public boolean hasData(String path) throws IOException, KeeperException, InterruptedException {
+    return false;
+  }
+
+  @Override
+  public List<String> listData(String path) throws NoSuchElementException, IOException, KeeperException, InterruptedException {
+    return null;
+  }
+
+  @Override
+  public List<String> listData(String path, Watcher watcher) throws NoSuchElementException, IOException, KeeperException, InterruptedException {
+    return null;
+  }
+
+  @Override
+  public VersionedData getData(String path, Watcher watcher) throws NoSuchElementException, IOException, KeeperException, InterruptedException {
+    return null;
+  }
+
+  @Override
+  public void makePath(String path) throws AlreadyExistsException, IOException, KeeperException, InterruptedException {
+
+  }
+
+  @Override
+  public void makePath(String path, byte[] data, CreateMode createMode, boolean failOnExists) throws AlreadyExistsException, IOException, KeeperException, InterruptedException {
+
+  }
+
+  @Override
+  public String createData(String path, byte[] data, CreateMode mode) throws AlreadyExistsException, IOException, KeeperException, InterruptedException {
+    return null;
+  }
+
+  @Override
+  public void removeData(String path, int version) throws NoSuchElementException, IOException, NotEmptyException, KeeperException, InterruptedException, BadVersionException {
+
+  }
+
+  @Override
+  public void setData(String path, byte[] data, int version) throws BadVersionException, NoSuchElementException, IOException, KeeperException, InterruptedException {
+
+  }
+
+  @Override
+  public List<OpResult> multi(Iterable<Op> ops) throws BadVersionException, NoSuchElementException, AlreadyExistsException, IOException, KeeperException, InterruptedException {
+    return null;
+  }
+
+  @Override
+  public AutoScalingConfig getAutoScalingConfig(Watcher watcher) throws InterruptedException, IOException {
+    return autoScalingConfig;
+  }
+
+  @Override
+  public void close() throws IOException {
+
+  }
+}
diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/policy8x/AssignerNodeStateProvider.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/policy8x/AssignerNodeStateProvider.java
new file mode 100644
index 0000000..8502a15
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/policy8x/AssignerNodeStateProvider.java
@@ -0,0 +1,56 @@
+package org.apache.solr.cloud.api.collections.assign.policy8x;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.solr.client.solrj.cloud.NodeStateProvider;
+import org.apache.solr.client.solrj.cloud.autoscaling.Variable;
+import org.apache.solr.cloud.api.collections.assign.AssignerClusterState;
+import org.apache.solr.cloud.api.collections.assign.AssignerNodeState;
+import org.apache.solr.common.cloud.Replica;
+import org.apache.solr.common.util.Utils;
+
+/**
+ *
+ */
+public class AssignerNodeStateProvider implements NodeStateProvider {
+
+  private final AssignerClusterState state;
+
+  public AssignerNodeStateProvider(AssignerClusterState state) {
+    this.state = state;
+  }
+
+  @Override
+  public Map<String, Object> getNodeValues(String node, Collection<String> tags) {
+    AssignerNodeState nodeState = state.getNodeState(node, tags, Collections.emptyList());
+    Map<String, Object> values = new HashMap<>();
+    values.putAll(nodeState.getProperties());
+    values.put(Variable.Type.FREEDISK.tagName, nodeState.getFreeDiskGB());
+    values.put(Variable.Type.TOTALDISK.tagName, nodeState.getTotalDiskGB());
+    return values;
+  }
+
+  @Override
+  public Map<String, Map<String, List<Replica>>> getReplicaInfo(String node, Collection<String> keys) {
+    AssignerNodeState nodeState = state.getNodeState(node, Collections.emptyList(), keys);
+    Map<String, Map<String, List<Replica>>> replicas = new HashMap<>();
+    nodeState.getReplicas().forEach(ar -> {
+      Map<String, List<Replica>> perColl = replicas.computeIfAbsent(ar.getCollection(), Utils.NEW_HASHMAP_FUN);
+      List<Replica> perShard = perColl.computeIfAbsent(ar.getShard(), Utils.NEW_ARRAYLIST_FUN);
+      Replica r = new Replica(ar.getName(), ar.getNode(), ar.getCollection(), ar.getShard(),
+          ar.getCore(), ar.getState(), ar.getType(), ar.getProperties());
+      perShard.add(r);
+    });
+    return replicas;
+  }
+
+  @Override
+  public void close() throws IOException {
+
+  }
+}
diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/policy8x/Policy8xAssigner.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/policy8x/Policy8xAssigner.java
new file mode 100644
index 0000000..14e9b7a
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/policy8x/Policy8xAssigner.java
@@ -0,0 +1,61 @@
+package org.apache.solr.cloud.api.collections.assign.policy8x;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.solr.client.solrj.cloud.autoscaling.AutoScalingConfig;
+import org.apache.solr.client.solrj.cloud.autoscaling.PolicyHelper;
+import org.apache.solr.cloud.api.collections.assign.AddReplicaDecision;
+import org.apache.solr.cloud.api.collections.assign.AddReplicaRequest;
+import org.apache.solr.cloud.api.collections.assign.Assigner;
+import org.apache.solr.cloud.api.collections.assign.AssignerClusterState;
+import org.apache.solr.cloud.api.collections.assign.AssignerException;
+import org.apache.solr.cloud.api.collections.assign.AssignDecision;
+import org.apache.solr.cloud.api.collections.assign.AssignDecisions;
+import org.apache.solr.cloud.api.collections.assign.AssignRequest;
+import org.apache.solr.cloud.api.collections.assign.NoopDecision;
+import org.apache.solr.common.cloud.Replica;
+import org.apache.solr.common.cloud.ReplicaPosition;
+import org.apache.solr.common.util.TimeSource;
+
+/**
+ *
+ */
+public class Policy8xAssigner implements Assigner {
+
+  private TimeSource timeSource = TimeSource.NANO_TIME;
+
+  @Override
+  public AssignDecisions computeAssignments(AssignerClusterState initialState,
+                                            List<AssignRequest> requests) throws InterruptedException, AssignerException {
+    AssignerCloudManager cloudManager = new AssignerCloudManager(initialState, timeSource);
+    try {
+      AutoScalingConfig autoScalingConfig = cloudManager.getDistribStateManager().getAutoScalingConfig();
+      List<AssignDecision> decisions = new ArrayList<>();
+      // XXX this handles only Add requests
+      for (AssignRequest req : requests) {
+        if (req instanceof AddReplicaRequest) {
+          AddReplicaRequest areq = (AddReplicaRequest) req;
+          int nrtReplicas = areq.getType() == Replica.Type.NRT ? 1 : 0;
+          int tlogReplicas = areq.getType() == Replica.Type.TLOG ? 1 : 0;
+          int pullReplicas = areq.getType() == Replica.Type.PULL ? 1 : 0;
+          List<ReplicaPosition> positions = PolicyHelper.getReplicaLocations(areq.getCollection(), autoScalingConfig,
+              cloudManager, null, Collections.singletonList(areq.getShard()),
+              nrtReplicas, tlogReplicas, pullReplicas, areq.getNodeSet() != null ? new ArrayList(areq.getNodeSet()) : null);
+          positions.forEach(pos -> {
+            decisions.add(new AddReplicaDecision(req, pos.node));
+          });
+        } else {
+          decisions.add(NoopDecision.of("unsupported", req));
+        }
+      }
+      // XXX we should return the new state here
+      return new AssignDecisions(initialState, decisions);
+    } catch (IOException e) {
+      throw new AssignerException(e);
+    }
+  }
+}
diff --git a/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/policy8x/SolrCloudManagerAssignerClusterState.java b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/policy8x/SolrCloudManagerAssignerClusterState.java
new file mode 100644
index 0000000..e798602
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/cloud/api/collections/assign/policy8x/SolrCloudManagerAssignerClusterState.java
@@ -0,0 +1,159 @@
+package org.apache.solr.cloud.api.collections.assign.policy8x;
+
+import java.io.IOException;
+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.stream.Collectors;
+
+import org.apache.solr.client.solrj.cloud.NodeStateProvider;
+import org.apache.solr.client.solrj.cloud.SolrCloudManager;
+import org.apache.solr.client.solrj.cloud.autoscaling.AutoScalingConfig;
+import org.apache.solr.cloud.api.collections.assign.AssignerClusterState;
+import org.apache.solr.cloud.api.collections.assign.AssignerCollectionState;
+import org.apache.solr.cloud.api.collections.assign.AssignerNodeState;
+import org.apache.solr.cloud.api.collections.assign.AssignerReplica;
+import org.apache.solr.cloud.api.collections.assign.AssignerShardState;
+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.util.Utils;
+
+/**
+ *
+ */
+public class SolrCloudManagerAssignerClusterState implements AssignerClusterState {
+  private final Map<String, Object> properties = new HashMap<>();
+  private final SolrCloudManager cloudManager;
+
+  public SolrCloudManagerAssignerClusterState(SolrCloudManager cloudManager) throws Exception {
+    this.cloudManager = cloudManager;
+    AutoScalingConfig autoScalingConfig = cloudManager.getDistribStateManager().getAutoScalingConfig();
+    properties.put(ZkStateReader.SOLR_AUTOSCALING_CONF_PATH, Utils.toJSONString(autoScalingConfig));
+  }
+
+  @Override
+  public Set<String> getNodes() {
+    return cloudManager.getClusterStateProvider().getLiveNodes();
+  }
+
+  @Override
+  public Set<String> getLiveNodes() {
+    return cloudManager.getClusterStateProvider().getLiveNodes();
+  }
+
+  @Override
+  public Collection<String> getCollections() {
+    try {
+      return cloudManager.getClusterStateProvider().getClusterState().getCollectionStates().keySet();
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+  @Override
+  public AssignerNodeState getNodeState(String nodeName, Collection<String> nodeKeys, Collection<String> replicaKeys) {
+    final NodeStateProvider nodeStateProvider = cloudManager.getNodeStateProvider();
+    return new AssignerNodeState() {
+      @Override
+      public Collection<AssignerReplica> getReplicas() {
+        Map<String, Map<String, List<Replica>>> replicaInfos = nodeStateProvider.getReplicaInfo(nodeName, replicaKeys);
+        List<AssignerReplica> replicas = new ArrayList<>();
+        replicaInfos.forEach((coll, shards) -> {
+          shards.forEach((shard, infos) -> {
+            infos.forEach(info -> {
+              AssignerReplica ar = new AssignerReplica(info.getName(), info.getNodeName(), info.getCollection(),
+                  info.getShard(), info.getCoreName(), info.getType(), info.getState(),
+                  info.getProperties());
+              replicas.add(ar);
+            });
+          });
+        });
+        return replicas;
+      }
+
+      @Override
+      public long getTotalDiskGB() {
+        return -1;
+      }
+
+      @Override
+      public long getFreeDiskGB() {
+        return -1;
+      }
+
+      @Override
+      public Map<String, Object> getProperties() {
+        return nodeStateProvider.getNodeValues(nodeName, nodeKeys);
+      }
+    };
+  }
+
+  @Override
+  public AssignerCollectionState getCollectionState(String collectionName, Collection<String> replicaKeys) {
+    ClusterState.CollectionRef collRef = cloudManager.getClusterStateProvider().getState(collectionName);
+    if (collRef == null) {
+      return null;
+    }
+    final DocCollection coll = collRef.get();
+    return new AssignerCollectionState() {
+      @Override
+      public String getCollection() {
+        return collectionName;
+      }
+
+      @Override
+      public Collection<String> getShards() {
+        return coll.getSlicesMap().keySet();
+      }
+
+      @Override
+      public AssignerShardState getShardState(String shardName) {
+        Slice slice = coll.getSlice(shardName);
+        if (slice == null) {
+          return null;
+        }
+        return new AssignerShardState() {
+          @Override
+          public String getName() {
+            return slice.getName();
+          }
+
+          @Override
+          public Collection<AssignerReplica> getReplicas() {
+            return slice.getReplicas().stream()
+                .map(r -> new AssignerReplica(
+                    r.getName(),
+                    r.getNodeName(),
+                    r.getCollection(),
+                    r.getShard(),
+                    r.getCoreName(),
+                    r.getType(),
+                    r.getState(),
+                    r.getProperties())).collect(Collectors.toList());
+          }
+
+          @Override
+          public Map<String, Object> getProperties() {
+            return slice.getProperties();
+          }
+        };
+      }
+
+      @Override
+      public Map<String, Object> getProperties() {
+        return coll.getProperties();
+      }
+    };
+  }
+
+  @Override
+  public Map<String, Object> getProperties() {
+    return properties;
+  }
+}
diff --git a/solr/core/src/test/org/apache/solr/cloud/api/collections/assign/policy8x/TestPolicy8x.java b/solr/core/src/test/org/apache/solr/cloud/api/collections/assign/policy8x/TestPolicy8x.java
new file mode 100644
index 0000000..3368dfb
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/cloud/api/collections/assign/policy8x/TestPolicy8x.java
@@ -0,0 +1,44 @@
+package org.apache.solr.cloud.api.collections.assign.policy8x;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.solr.SolrTestCaseJ4;
+import org.apache.solr.client.solrj.cloud.SolrCloudManager;
+import org.apache.solr.cloud.api.collections.assign.AddReplicaRequest;
+import org.apache.solr.cloud.api.collections.assign.AssignDecisions;
+import org.apache.solr.cloud.api.collections.assign.AssignRequest;
+import org.apache.solr.cloud.autoscaling.sim.SimCloudManager;
+import org.apache.solr.common.cloud.Replica;
+import org.apache.solr.common.util.TimeSource;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class TestPolicy8x extends SolrTestCaseJ4 {
+
+  SolrCloudManager cloudManager;
+
+  @Before
+  public void initCluster() throws Exception {
+    cloudManager = SimCloudManager.createCluster(100, TimeSource.get("simTime:50"));
+  }
+
+  @After
+  public void shutdownCluster() throws Exception {
+    if (cloudManager != null) {
+      cloudManager.close();
+    }
+  }
+
+  @Test
+  public void testBasics() throws Exception {
+    SolrCloudManagerAssignerClusterState initialState = new SolrCloudManagerAssignerClusterState(cloudManager);
+    Policy8xAssigner assigner = new Policy8xAssigner();
+    List<AssignRequest> requests = Collections.singletonList(new AddReplicaRequest("foo", "bar", Replica.Type.NRT, null, null, "c1", null));
+    AssignDecisions decisions = assigner.computeAssignments(initialState, requests);
+  }
+}