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:08 UTC

[lucene-solr] branch jira/solr-14613 created (now 5f6887d)

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

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


      at 5f6887d  SOLR-14613: Initial POC based on Ilan's proposal.

This branch includes the following new commits:

     new 5f6887d  SOLR-14613: Initial POC based on Ilan's proposal.

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



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

Posted by ab...@apache.org.
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);
+  }
+}