You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by da...@apache.org on 2018/08/06 04:16:03 UTC

[30/48] lucene-solr:jira/http2: SOLR-12592: support #EQUAL function in cores in autoscaling policies

SOLR-12592: support #EQUAL function in cores in autoscaling policies


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

Branch: refs/heads/jira/http2
Commit: 868e970816d8bb52f138a1181416438c348c750e
Parents: 259bc2b
Author: Noble Paul <no...@apache.org>
Authored: Thu Aug 2 15:20:46 2018 +1000
Committer: Noble Paul <no...@apache.org>
Committed: Thu Aug 2 15:20:46 2018 +1000

----------------------------------------------------------------------
 solr/CHANGES.txt                                |  2 +
 .../autoscaling/AutoScalingHandlerTest.java     |  2 +-
 .../client/solrj/cloud/autoscaling/Clause.java  | 15 +++-
 .../solrj/cloud/autoscaling/CoresVariable.java  | 48 ++++++++++---
 .../cloud/autoscaling/ReplicaVariable.java      |  4 ++
 .../solrj/cloud/autoscaling/Variable.java       | 43 ++++++------
 .../solrj/cloud/autoscaling/TestPolicy.java     | 55 ++++++++++-----
 .../solrj/cloud/autoscaling/TestPolicy2.java    | 74 +++++++++++++++++++-
 8 files changed, 192 insertions(+), 51 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/868e9708/solr/CHANGES.txt
----------------------------------------------------------------------
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 6864ce7..93171af 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -131,6 +131,8 @@ New Features
 
 * SOLR-12402: Factor out SolrDefaultStreamFactory class. (Christine Poerschke)
 
+* SOLR-12592: support #EQUAL function in cores in autoscaling policies (noble)
+
 Bug Fixes
 ----------------------
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/868e9708/solr/core/src/test/org/apache/solr/cloud/autoscaling/AutoScalingHandlerTest.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/AutoScalingHandlerTest.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/AutoScalingHandlerTest.java
index afce5a4..5b8ec21 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/AutoScalingHandlerTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/AutoScalingHandlerTest.java
@@ -769,7 +769,7 @@ public class AutoScalingHandlerTest extends SolrCloudTestCase {
       assertEquals(6, node.size());
       assertNotNull(node.get("node"));
       assertNotNull(node.get("cores"));
-      assertEquals(0L, node.get("cores"));
+      assertEquals(0d, node.get("cores"));
       assertNotNull(node.get("freedisk"));
       assertTrue(node.get("freedisk") instanceof Double);
       assertNotNull(node.get("sysLoadAvg"));

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/868e9708/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/Clause.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/Clause.java b/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/Clause.java
index 5fd9f8b..5fe6894 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/Clause.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/Clause.java
@@ -382,10 +382,12 @@ public class Clause implements MapWriter, Comparable<Clause> {
       }
     } else {
       for (Row r : session.matrix) {
+        computedValueEvaluator.node = r.node;
         SealedClause sealedClause = getSealedClause(computedValueEvaluator);
         if (!sealedClause.getGlobalTag().isPass(r)) {
           sealedClause.getGlobalTag().varType.addViolatingReplicas(ctx.reset(null, null,
-              new Violation(sealedClause, null, null, r.node, r.getVal(sealedClause.globalTag.name), sealedClause.globalTag.delta(r.getVal(globalTag.name)), null)));
+              new Violation(sealedClause, null, null, r.node, r.getVal(sealedClause.globalTag.name),
+                  sealedClause.globalTag.delta(r.getVal(globalTag.name)), null)));
         }
       }
     }
@@ -428,6 +430,17 @@ public class Clause implements MapWriter, Comparable<Clause> {
         }
       }
     }
+
+    if (this.getTag().op != LESS_THAN && this.getTag().varType == Type.NODE) {
+      collVsShardVsTagVsCount.forEach((coll, shardVsNodeVsCount) ->
+          shardVsNodeVsCount.forEach((shard, nodeVsCount) -> {
+            for (Row row : allRows) {
+              if (!nodeVsCount.containsKey(row.node)) {
+                nodeVsCount.put(row.node, new ReplicaCount());
+              }
+            }
+          }));
+    }
     return collVsShardVsTagVsCount;
   }
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/868e9708/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/CoresVariable.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/CoresVariable.java b/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/CoresVariable.java
index 577717f..45f9eb7 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/CoresVariable.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/CoresVariable.java
@@ -19,8 +19,6 @@ package org.apache.solr.client.solrj.cloud.autoscaling;
 
 import java.util.function.Consumer;
 
-import org.apache.solr.common.cloud.rule.ImplicitSnitch;
-
 import static org.apache.solr.common.params.CollectionParams.CollectionAction.MOVEREPLICA;
 
 public class CoresVariable extends VariableBase {
@@ -35,14 +33,15 @@ public class CoresVariable extends VariableBase {
 
   @Override
   public void addViolatingReplicas(Violation.Ctx ctx) {
-    for (Row r : ctx.allRows) {
-      if (!ctx.clause.tag.isPass(r)) {
-        r.forEachReplica(replicaInfo -> ctx.currentViolation
+    for (Row row : ctx.allRows) {
+      if (row.node.equals(ctx.currentViolation.node)) {
+        row.forEachReplica(replicaInfo -> ctx.currentViolation
             .addReplica(new Violation.ReplicaInfoAndErr(replicaInfo)
-                .withDelta(ctx.clause.tag.delta(r.getVal(ImplicitSnitch.CORES)))));
+                .withDelta(ctx.currentViolation.replicaCountDelta)));
       }
     }
 
+
   }
 
   @Override
@@ -52,18 +51,49 @@ public class CoresVariable extends VariableBase {
       for (int i = 0; i < Math.abs(ctx.violation.replicaCountDelta); i++) {
         Suggester suggester = ctx.session.getSuggester(MOVEREPLICA)
             .hint(Suggester.Hint.SRC_NODE, ctx.violation.node);
-        ctx.addSuggestion(suggester);
+        if (ctx.addSuggestion(suggester) == null) break;
       }
     }
   }
 
   @Override
   public void projectAddReplica(Cell cell, ReplicaInfo ri, Consumer<Row.OperationInfo> ops, boolean strictMode) {
-    cell.val = cell.val == null ? 0 : ((Number) cell.val).longValue() + 1;
+    cell.val = cell.val == null ? 0 : ((Number) cell.val).doubleValue() + 1;
   }
 
   @Override
   public void projectRemoveReplica(Cell cell, ReplicaInfo ri, Consumer<Row.OperationInfo> opCollector) {
-    cell.val = cell.val == null ? 0 : ((Number) cell.val).longValue() - 1;
+    cell.val = cell.val == null ? 0 : ((Number) cell.val).doubleValue() - 1;
+  }
+
+  @Override
+  public Object computeValue(Policy.Session session, Clause.Condition condition, String collection, String shard, String node) {
+    if (condition.computedType == Clause.ComputedType.EQUAL) {
+      int[] coresCount = new int[1];
+      int[] liveNodes = new int[1];
+      for (Row row : session.matrix) {
+        if (!row.isLive) continue;
+        liveNodes[0]++;
+        row.forEachReplica(replicaInfo -> coresCount[0]++);
+      }
+      return liveNodes[0] == 0 || coresCount[0] == 0 ? 0d : (double) coresCount[0] / (double) liveNodes[0];
+    } else {
+      throw new IllegalArgumentException("Invalid computed type in " + condition);
+    }
+  }
+
+  @Override
+  public String postValidate(Clause.Condition condition) {
+    Clause.Condition nodeTag = condition.getClause().getTag();
+    if (nodeTag.name.equals("node") && nodeTag.op == Operand.WILDCARD) {
+      return null;
+    } else {
+      throw new IllegalArgumentException("cores: '#EQUAL' can be used only with node: '#ANY'");
+    }
+  }
+
+  @Override
+  public Operand getOperand(Operand expected, Object strVal, Clause.ComputedType computedType) {
+    return ReplicaVariable.checkForRangeOperand(expected, strVal, computedType);
   }
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/868e9708/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/ReplicaVariable.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/ReplicaVariable.java b/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/ReplicaVariable.java
index 2f66609..ab0a03a 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/ReplicaVariable.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/ReplicaVariable.java
@@ -52,6 +52,10 @@ class ReplicaVariable extends VariableBase {
   @Override
   public Operand getOperand(Operand expected, Object strVal, Clause.ComputedType computedType) {
     if (computedType == Clause.ComputedType.ALL) return expected;
+    return checkForRangeOperand(expected, strVal, computedType);
+  }
+
+  static Operand checkForRangeOperand(Operand expected, Object strVal, Clause.ComputedType computedType) {
     if (strVal instanceof String) {
       String s = ((String) strVal).trim();
       int hyphenIdx = s.indexOf('-');

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/868e9708/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/Variable.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/Variable.java b/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/Variable.java
index 8df74bf..c3d8ca2 100644
--- a/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/Variable.java
+++ b/solr/solrj/src/java/org/apache/solr/client/solrj/cloud/autoscaling/Variable.java
@@ -86,7 +86,7 @@ public interface Variable {
 
     @Meta(name = "collection",
         type = String.class)
-    COLL(),
+    COLL,
     @Meta(
         name = "shard",
         type = String.class,
@@ -98,7 +98,7 @@ public interface Variable {
         min = 0, max = -1,
         implementation = ReplicaVariable.class,
         computedValues = {Clause.ComputedType.EQUAL, Clause.ComputedType.PERCENT, Clause.ComputedType.ALL})
-    REPLICA(),
+    REPLICA,
     @Meta(name = ImplicitSnitch.PORT,
         type = Long.class,
         min = 1,
@@ -106,35 +106,35 @@ public interface Variable {
         supportArrayVals = true,
         wildCards = Policy.EACH
     )
-    PORT(),
+    PORT,
     @Meta(name = "ip_1",
         type = Long.class,
         min = 0,
         max = 255,
         supportArrayVals = true,
         wildCards = Policy.EACH)
-    IP_1(),
+    IP_1,
     @Meta(name = "ip_2",
         type = Long.class,
         min = 0,
         max = 255,
         supportArrayVals = true,
         wildCards = Policy.EACH)
-    IP_2(),
+    IP_2,
     @Meta(name = "ip_3",
         type = Long.class,
         min = 0,
         max = 255,
         supportArrayVals = true,
         wildCards = Policy.EACH)
-    IP_3(),
+    IP_3,
     @Meta(name = "ip_4",
         type = Long.class,
         min = 0,
         max = 255,
         supportArrayVals = true,
         wildCards = Policy.EACH)
-    IP_4(),
+    IP_4,
     @Meta(name = ImplicitSnitch.DISK,
         type = Double.class,
         min = 0,
@@ -143,12 +143,12 @@ public interface Variable {
         associatedPerNodeValue = "totaldisk",
         implementation = FreeDiskVariable.class,
         computedValues = Clause.ComputedType.PERCENT)
-    FREEDISK(),
+    FREEDISK,
 
     @Meta(name = "totaldisk",
         type = Double.class,
         isHidden = true, implementation = VariableBase.TotalDiskVariable.class)
-    TOTALDISK(),
+    TOTALDISK,
 
     @Meta(name = Variable.coreidxsize,
         type = Double.class,
@@ -157,40 +157,41 @@ public interface Variable {
         min = 0,
         implementation = VariableBase.CoreIndexSizeVariable.class,
         metricsKey = "INDEX.sizeInBytes")
-    CORE_IDX(),
+    CORE_IDX,
     @Meta(name = ImplicitSnitch.NODEROLE,
         type = String.class,
         enumVals = "overseer")
-    NODE_ROLE(),
+    NODE_ROLE,
 
     @Meta(name = ImplicitSnitch.CORES,
-        type = Long.class,
-        min = 0,
+        type = Double.class,
+        min = 0, max = -1,
+        computedValues = Clause.ComputedType.EQUAL,
         implementation = CoresVariable.class)
-    CORES(),
+    CORES,
 
     @Meta(name = ImplicitSnitch.SYSLOADAVG,
         type = Double.class,
         min = 0,
         max = 100,
         isNodeSpecificVal = true)
-    SYSLOADAVG(),
+    SYSLOADAVG,
 
     @Meta(name = ImplicitSnitch.HEAPUSAGE,
         type = Double.class,
         min = 0,
         isNodeSpecificVal = true)
-    HEAPUSAGE(),
+    HEAPUSAGE,
     @Meta(name = "NUMBER",
         type = Long.class,
         min = 0)
-    NUMBER(),
+    NUMBER,
 
     @Meta(name = "STRING",
         type = String.class,
         wildCards = Policy.EACH,
         supportArrayVals = true)
-    STRING(),
+    STRING,
 
     @Meta(name = "node",
         type = String.class,
@@ -198,19 +199,19 @@ public interface Variable {
         wildCards = {Policy.ANY, Policy.EACH},
         implementation = NodeVariable.class,
         supportArrayVals = true)
-    NODE(),
+    NODE,
 
     @Meta(name = "LAZY",
         type = void.class,
         implementation = VariableBase.LazyVariable.class)
-    LAZY(),
+    LAZY,
 
     @Meta(name = ImplicitSnitch.DISKTYPE,
         type = String.class,
         enumVals = {"ssd", "rotational"},
         implementation = VariableBase.DiskTypeVariable.class,
         supportArrayVals = true)
-    DISKTYPE();
+    DISKTYPE;
 
     public final String tagName;
     public final Class type;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/868e9708/solr/solrj/src/test/org/apache/solr/client/solrj/cloud/autoscaling/TestPolicy.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/cloud/autoscaling/TestPolicy.java b/solr/solrj/src/test/org/apache/solr/client/solrj/cloud/autoscaling/TestPolicy.java
index 16dfdcd..48d8f2e 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/cloud/autoscaling/TestPolicy.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/cloud/autoscaling/TestPolicy.java
@@ -869,6 +869,11 @@ public class TestPolicy extends SolrTestCaseJ4 {
     expectThrows(IllegalArgumentException.class,
         () -> Clause.create("{replica: '#EQUAL' , shard: '#EACH' , sysprop.zone:[east]}"));
 
+    clause = Clause.create("{cores: '#EQUAL' , node:'#ANY'}");
+    assertEquals(Clause.ComputedType.EQUAL, clause.globalTag.computedType);
+    expectThrows(IllegalArgumentException.class,
+        () -> Clause.create("{cores: '#EQUAL' , node:'node1'}"));
+
   }
 
 
@@ -997,20 +1002,30 @@ public class TestPolicy extends SolrTestCaseJ4 {
       }
     });
     List<Violation> violations = session.getViolations();
-    assertEquals(1, violations.size());
-    Violation violation = violations.get(0);
-    assertEquals("node1", violation.node);
-    RangeVal val = (RangeVal) violation.getClause().replica.val;
-    assertEquals(1.0d, val.min.doubleValue(), 0.01);
-    assertEquals(2.0, val.max.doubleValue(), 0.01);
-    assertEquals(1.2d, val.actual.doubleValue(), 0.01d);
-    assertEquals(1, violation.replicaCountDelta.doubleValue(), 0.01);
-    assertEquals(3, violation.getViolatingReplicas().size());
-    Set<String> expected = ImmutableSet.of("r1", "r3", "r5");
-    for (Violation.ReplicaInfoAndErr replicaInfoAndErr : violation.getViolatingReplicas()) {
-      assertTrue(expected.contains(replicaInfoAndErr.replicaInfo.getCore()));
+    assertEquals(2, violations.size());
+    for (Violation violation : violations) {
+      if (violation.node.equals("node1")) {
+        RangeVal val = (RangeVal) violation.getClause().replica.val;
+        assertEquals(1.0d, val.min.doubleValue(), 0.01);
+        assertEquals(2.0, val.max.doubleValue(), 0.01);
+        assertEquals(1.2d, val.actual.doubleValue(), 0.01d);
+        assertEquals(1, violation.replicaCountDelta.doubleValue(), 0.01);
+        assertEquals(3, violation.getViolatingReplicas().size());
+        Set<String> expected = ImmutableSet.of("r1", "r3", "r5");
+        for (Violation.ReplicaInfoAndErr replicaInfoAndErr : violation.getViolatingReplicas()) {
+          assertTrue(expected.contains(replicaInfoAndErr.replicaInfo.getCore()));
+        }
+      } else if (violation.node.equals("node5")) {
+        assertEquals(-1, violation.replicaCountDelta.doubleValue(), 0.01);
+
+      } else{
+        fail();
+      }
     }
-    System.out.println();
+//    Violation violation = violations.get(0);
+//    assertEquals("node1", violation.node);
+
+
 
   }
 
@@ -2341,9 +2356,16 @@ public class TestPolicy extends SolrTestCaseJ4 {
     AutoScalingConfig autoScalingConfig = new AutoScalingConfig((Map<String, Object>) Utils.fromJSONString(autoScalingjson));
     Policy.Session session = autoScalingConfig.getPolicy().createSession(cloudManagerWithData(dataproviderdata));
     List<Violation> violations = session.getViolations();
-    assertEquals(1, violations.size());
-    assertEquals(1.0d, violations.get(0).replicaCountDelta, 0.01);
-    assertEquals(1.53d, ((RangeVal) violations.get(0).getClause().getReplica().val).actual);
+    assertEquals(2, violations.size());
+    for (Violation violation : violations) {
+      if(violation.node.equals("10.0.0.6:8983_solr")){
+        assertEquals(1.0d, violation.replicaCountDelta, 0.01);
+        assertEquals(1.53d, ((RangeVal) violation.getClause().getReplica().val).actual);
+      } else if(violation.node.equals("10.0.0.6:7574_solr")){
+        assertEquals(-1.0d, violation.replicaCountDelta, 0.01);
+      }
+
+    }
 
 
     dataproviderdata = "{" +
@@ -2493,6 +2515,7 @@ public class TestPolicy extends SolrTestCaseJ4 {
       assertEquals(500d, r.delta, 0.1);
 
     }
+
     List<Suggester.SuggestionInfo> l = PolicyHelper.getSuggestions(cfg, cloudManagerWithData(dataproviderdata));
     assertEquals(3, l.size());
     Map m = l.get(0).toMap(new LinkedHashMap<>());

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/868e9708/solr/solrj/src/test/org/apache/solr/client/solrj/cloud/autoscaling/TestPolicy2.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/test/org/apache/solr/client/solrj/cloud/autoscaling/TestPolicy2.java b/solr/solrj/src/test/org/apache/solr/client/solrj/cloud/autoscaling/TestPolicy2.java
index 678600f..c1d69b9 100644
--- a/solr/solrj/src/test/org/apache/solr/client/solrj/cloud/autoscaling/TestPolicy2.java
+++ b/solr/solrj/src/test/org/apache/solr/client/solrj/cloud/autoscaling/TestPolicy2.java
@@ -27,6 +27,7 @@ import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import org.apache.solr.SolrTestCaseJ4;
@@ -106,7 +107,8 @@ public class TestPolicy2 extends SolrTestCaseJ4 {
             "    'node1':{'cores' : 3, 'freedisk' : 700, 'totaldisk' :1000, 'sysprop.zone' : 'east'}," +
             "    'node2':{'cores' : 1, 'freedisk' : 900, 'totaldisk' :1000, 'sysprop.zone' : 'west'}," +
             "    'node3':{'cores' : 1, 'freedisk' : 900, 'totaldisk' :1000, 'sysprop.zone': 'east'}," +
-            "    'node4':{'cores' : 1, 'freedisk' : 900, 'totaldisk' :1000, 'sysprop.zone': 'west'}" +
+            "    'node4':{'cores' : 1, 'freedisk' : 900, 'totaldisk' :1000, 'sysprop.zone': 'west'}," +
+            "    'node5':{'cores' : 0, 'freedisk' : 1000, 'totaldisk' :1000, 'sysprop.zone': 'west'}" +
             "  }," +
             "  'replicaValues':[" +
             "    {'INDEX.sizeInGB': 100, core : r1}," +
@@ -118,7 +120,7 @@ public class TestPolicy2 extends SolrTestCaseJ4 {
 
     String autoScalingjson = "{cluster-policy:[" +
         "    { replica : '<3' , shard : '#EACH', sysprop.zone: [east,west] } ]," +
-        "  'cluster-preferences':[{ minimize : cores},{minimize : freedisk, precision : 50}]}";
+        "  'cluster-preferences':[{ minimize : cores},{maximize : freedisk, precision : 50}]}";
     Policy policy = new Policy((Map<String, Object>) Utils.fromJSONString(autoScalingjson));
     Policy.Session session = policy.createSession(createCloudManager(state, metaData));
     List<Violation> violations = session.getViolations();
@@ -131,7 +133,7 @@ public class TestPolicy2 extends SolrTestCaseJ4 {
 
     autoScalingjson = "{cluster-policy:[" +
         "    { replica : '<3' , shard : '#EACH', sysprop.zone: '#EACH' } ]," +
-        "  'cluster-preferences':[{ minimize : cores},{minimize : freedisk, precision : 50}]}";
+        "  'cluster-preferences':[{ minimize : cores},{maximize : freedisk, precision : 50}]}";
     policy = new Policy((Map<String, Object>) Utils.fromJSONString(autoScalingjson));
     session = policy.createSession(createCloudManager(state, metaData));
     violations = session.getViolations();
@@ -141,6 +143,72 @@ public class TestPolicy2 extends SolrTestCaseJ4 {
     for (Violation.ReplicaInfoAndErr r : violations.get(0).getViolatingReplicas()) {
       assertEquals("shard2", r.replicaInfo.getShard());
     }
+    autoScalingjson = "{cluster-policy:[" +
+        "    { replica : '#EQUAL' , node: '#ANY' } ]," +
+        "  'cluster-preferences':[{ minimize : cores},{maximize : freedisk, precision : 50}]}";
+    policy = new Policy((Map<String, Object>) Utils.fromJSONString(autoScalingjson));
+    session = policy.createSession(createCloudManager(state, metaData));
+    violations = session.getViolations();
+    List<Suggester.SuggestionInfo> suggestions = null;
+    assertEquals(2, violations.size());
+    for (Violation violation : violations) {
+      if (violation.node.equals("node1")) {
+        assertEquals(1.0d, violation.replicaCountDelta, 0.001);
+        assertEquals(3, violation.getViolatingReplicas().size());
+      } else if (violation.node.equals("node5")) {
+        assertEquals(-1.0d, violation.replicaCountDelta, 0.001);
+        assertEquals(0, violation.getViolatingReplicas().size());
+      } else {
+        fail();
+      }
+    }
+    suggestions = PolicyHelper.getSuggestions(new AutoScalingConfig((Map<String, Object>) Utils.fromJSONString(autoScalingjson))
+        , createCloudManager(state, metaData));
+    assertEquals(1, suggestions.size());
+    String repName = (String) Utils.getObjectByPath(suggestions.get(0).operation, true, "command/move-replica/replica");
+
+    AtomicBoolean found = new AtomicBoolean(false);
+    session.getNode("node1").forEachReplica(replicaInfo -> {
+      if (replicaInfo.getName().equals(repName)) {
+        found.set(true);
+      }
+    });
+    assertTrue(found.get());
+
+    autoScalingjson = "{cluster-policy:[" +
+        "    { cores : '#EQUAL' , node: '#ANY' } ]," +
+        "  'cluster-preferences':[{ minimize : cores},{minimize : freedisk, precision : 50}]}";
+    policy = new Policy((Map<String, Object>) Utils.fromJSONString(autoScalingjson));
+    session = policy.createSession(createCloudManager(state, metaData));
+    violations = session.getViolations();
+    assertEquals(2, violations.size());
+    for (Violation violation : violations) {
+      if(violation.node.equals("node1")) {
+        assertEquals(1.0d, violation.replicaCountDelta, 0.001);
+        assertEquals(3, violation.getViolatingReplicas().size());
+      } else if(violation.node.equals("node5")){
+        assertEquals(-1.0d, violation.replicaCountDelta, 0.001);
+        assertEquals(0, violation.getViolatingReplicas().size());
+      } else {
+        fail();
+      }
+
+    }
+
+    suggestions = PolicyHelper.getSuggestions(new AutoScalingConfig((Map<String, Object>) Utils.fromJSONString(autoScalingjson)),
+        createCloudManager(state, metaData));
+    assertEquals(1, suggestions.size());
+    assertEquals("node5", Utils.getObjectByPath(suggestions.get(0).operation, true, "command/move-replica/targetNode"));
+
+    String rName = (String) Utils.getObjectByPath(suggestions.get(0).operation, true, "command/move-replica/replica");
+
+    found.set(false);
+    session.getNode("node1").forEachReplica(replicaInfo -> {
+      if (replicaInfo.getName().equals(rName)) {
+        found.set(true);
+      }
+    });
+    assertTrue(found.get());
 
   }