SOLR-10278: conditions tested

This commit is contained in:
Noble Paul 2017-03-27 18:29:38 +10:30
parent 6373aeea36
commit bec41550db
4 changed files with 124 additions and 49 deletions

View File

@ -26,17 +26,25 @@ import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.solr.common.MapWriter;
import org.apache.solr.common.params.CommonParams;
import org.apache.solr.common.util.Utils;
import org.apache.solr.recipe.RuleSorter.ReplicaStat;
import org.apache.solr.recipe.RuleSorter.Row;
import static java.util.Collections.singletonMap;
import static org.apache.solr.common.params.CoreAdminParams.COLLECTION;
import static org.apache.solr.common.params.CoreAdminParams.REPLICA;
import static org.apache.solr.common.params.CoreAdminParams.SHARD;
import static org.apache.solr.recipe.Clause.TestStatus.FAIL;
import static org.apache.solr.recipe.Clause.TestStatus.NOT_APPLICABLE;
import static org.apache.solr.recipe.Clause.TestStatus.PASS;
import static org.apache.solr.recipe.Operand.EQUAL;
import static org.apache.solr.recipe.Operand.GREATER_THAN;
import static org.apache.solr.recipe.Operand.LESS_THAN;
import static org.apache.solr.recipe.Operand.NOT_EQUAL;
import static org.apache.solr.recipe.Operand.WILDCARD;
import static org.apache.solr.recipe.RuleSorter.ANY;
import static org.apache.solr.recipe.RuleSorter.EACH;
public class Clause implements MapWriter {
@ -74,12 +82,18 @@ public class Clause implements MapWriter {
this.op = op;
}
boolean isMatch(Row row) {
return op.canMatch(val, row.getVal(name));
TestStatus match(Row row) {
return op.match(val, row.getVal(name));
}
boolean isMatch(Object inputVal) {
return op.canMatch(val, inputVal);
boolean isPass(Object inputVal) {
return op.match(val, inputVal) == PASS;
}
boolean isPass(Row row) {
return op.match(val, row.getVal(name)) == PASS;
}
}
Condition parse(String s, Map m) {
@ -89,8 +103,8 @@ public class Clause implements MapWriter {
String conditionName = s.trim();
String value = val == null ? null : String.valueOf(val).trim();
Operand operand = null;
if ((expectedVal = Operand.ANY.parse(value)) != null) {
operand = Operand.ANY;
if ((expectedVal = WILDCARD.parse(value)) != null) {
operand = WILDCARD;
} else if ((expectedVal = NOT_EQUAL.parse(value)) != null) {
operand = NOT_EQUAL;
} else if ((expectedVal = GREATER_THAN.parse(value)) != null) {
@ -111,26 +125,36 @@ public class Clause implements MapWriter {
TestStatus test(Row row) {
AtomicReference<TestStatus> result = new AtomicReference<>(TestStatus.NOT_APPLICABLE);
outer:
for (Map.Entry<String, Map<String, List<RuleSorter.ReplicaStat>>> e : row.replicaInfo.entrySet()) {
if (!collection.isMatch(e.getKey())) break;
AtomicReference<TestStatus> result = new AtomicReference<>(NOT_APPLICABLE);
for (Map.Entry<String, Map<String, List<ReplicaStat>>> colls : row.replicaInfo.entrySet()) {
if (!collection.isPass(colls.getKey()) || result.get() == FAIL) break;
int count = 0;
for (Map.Entry<String, List<RuleSorter.ReplicaStat>> e1 : e.getValue().entrySet()) {
if (!shard.isMatch(e1.getKey())) break;
count += e1.getValue().size();
if (shard.val.equals(RuleSorter.EACH) && count > 0 && replica.isMatch(count) && tag.isMatch(row)) {
result.set(TestStatus.FAIL);
continue outer;
for (Map.Entry<String, List<ReplicaStat>> shards : colls.getValue().entrySet()) {
if (!shard.isPass(shards.getKey()) || result.get() == FAIL) break;
count += shards.getValue().size();
if (shard.val.equals(EACH)) testReplicaCount(row, result, count);
if (EACH.equals(shard.val)) count = 0;
}
if (RuleSorter.EACH.equals(shard.val)) count = 0;
if (shard.val.equals(ANY)) testReplicaCount(row, result, count);
}
if (shard.val.equals(RuleSorter.ANY) && count > 0 && replica.isMatch(count) && !tag.isMatch(row)) {
result.set(TestStatus.FAIL);
if (result.get() == FAIL) row.violations.add(this);
return result.get();
}
private void testReplicaCount(Row row, AtomicReference<TestStatus> result, int count) {
if("node".equals(tag.name)) if(!tag.isPass(row.node)) return;
boolean checkCount = replica.op.match(replica.val, 0) != PASS || count > 0;
if (replica.op == WILDCARD && count > 0 && !tag.isPass(row)) {
result.set(FAIL);
} else if (checkCount && !replica.isPass(count)) {
if (tag.op != WILDCARD && tag.isPass(row)) {
result.set(FAIL);
} else {
result.set(FAIL);
}
}
if (result.get() == TestStatus.FAIL) row.violations.add(this);
return result.get();
}
public boolean isStrict() {

View File

@ -19,25 +19,29 @@ package org.apache.solr.recipe;
import java.util.Objects;
import org.apache.solr.recipe.Clause.TestStatus;
import static org.apache.solr.recipe.Clause.TestStatus.*;
import static org.apache.solr.recipe.RuleSorter.ANY;
public enum Operand {
ANY(""){
WILDCARD(ANY){
@Override
public boolean canMatch(Object ruleVal, Object testVal) {
return true;
public TestStatus match(Object ruleVal, Object testVal) {
return testVal == null ? NOT_APPLICABLE : PASS;
}
@Override
public Object parse(String val) {
if(val == null) return RuleSorter.ANY;
return RuleSorter.ANY.equals(val) || RuleSorter.EACH.equals(val) ? val : null;
if(val == null) return ANY;
return ANY.equals(val) || RuleSorter.EACH.equals(val) ? val : null;
}
},
EQUAL(""),
NOT_EQUAL("!") {
@Override
public boolean canMatch(Object ruleVal, Object testVal) {
return !super.canMatch(ruleVal, testVal);
public TestStatus match(Object ruleVal, Object testVal) {
return super.match(ruleVal, testVal) == PASS ? FAIL : PASS;
}
},
GREATER_THAN(">") {
@ -48,16 +52,18 @@ public enum Operand {
@Override
public boolean canMatch(Object ruleVal, Object testVal) {
return testVal != null && compareNum(ruleVal, testVal) == -1;
public TestStatus match(Object ruleVal, Object testVal) {
if (testVal == null) return NOT_APPLICABLE;
return compareNum(ruleVal, testVal) == 1 ? PASS : FAIL;
}
},
LESS_THAN("<") {
@Override
public boolean canMatch(Object ruleVal, Object testVal) {
return testVal != null && compareNum(ruleVal, testVal) != -1;
public TestStatus match(Object ruleVal, Object testVal) {
if (testVal == null) return NOT_APPLICABLE;
return compareNum(ruleVal, testVal) == -1 ? PASS : FAIL;
}
@Override
@ -89,8 +95,8 @@ public enum Operand {
return val.startsWith(operand) ? val.substring(1) : null;
}
public boolean canMatch(Object ruleVal, Object testVal) {
return Objects.equals(String.valueOf(ruleVal), String.valueOf(testVal));
public TestStatus match(Object ruleVal, Object testVal) {
return Objects.equals(String.valueOf(ruleVal), String.valueOf(testVal)) ? PASS : FAIL;
}

View File

@ -238,6 +238,11 @@ public class RuleSorter {
for (Cell cell : cells) if (cell.name.equals(name)) return cell.val;
return null;
}
@Override
public String toString() {
return node;
}
}
static class Cell implements MapWriter {

View File

@ -32,9 +32,27 @@ import org.apache.solr.common.util.Utils;
import org.apache.solr.common.util.ValidatingJsonMap;
public class TestRuleSorter extends SolrTestCaseJ4 {
public void testOperands() {
Clause c = new Clause((Map<String, Object>) Utils.fromJSONString("{replica:'<2', node:'#ANY'}"));
assertFalse(c.replica.isPass(3));
assertFalse(c.replica.isPass(2));
assertTrue(c.replica.isPass(1));
c = new Clause((Map<String, Object>) Utils.fromJSONString("{replica:'>2', node:'#ANY'}"));
assertTrue(c.replica.isPass(3));
assertFalse(c.replica.isPass(2));
assertFalse(c.replica.isPass(1));
c = new Clause((Map<String, Object>) Utils.fromJSONString("{nodeRole:'!overseer'}"));
assertTrue(c.tag.isPass("OVERSEER"));
assertFalse(c.tag.isPass("overseer"));
}
public void testRuleParsing() throws IOException {
String rules = "{" +
"conditions:[{nodeRole:'!overseer', strict:false}, " +
"conditions:[{nodeRole:'!overseer', strict:false},{replica:'<1',node:node3}," +
"{replica:'<2',node:'#ANY', shard:'#EACH'}]," +
" preferences:[" +
"{minimize:cores , precision:2}," +
@ -46,38 +64,46 @@ public class TestRuleSorter extends SolrTestCaseJ4 {
"node1:{cores:12, freedisk: 334, heap:10480}," +
"node2:{cores:4, freedisk: 749, heap:6873}," +
"node3:{cores:7, freedisk: 262, heap:7834}," +
"node4:{cores:8, freedisk: 375, heap:16900}" +
"node4:{cores:8, freedisk: 375, heap:16900, nodeRole:overseer}" +
"}");
String clusterState = "{'gettingstarted':{" +
" 'router':{'name':'compositeId'}," +
" 'shards':{" +
" 'shard1':{" +
" 'range':'80000000-ffffffff'," +
" 'state':'active'," +
" 'replicas':{" +
" 'core_node1':{" +
" 'core':'gettingstarted_shard1_replica1'," +
" 'r1':{" +
" 'core':r1," +
" 'base_url':'http://10.0.0.4:8983/solr'," +
" 'node_name':'node1'," +
" 'state':'active'," +
" 'leader':'true'}," +
" 'core_node4':{" +
" 'core':'gettingstarted_shard1_replica2'," +
" 'r2':{" +
" 'core':r2," +
" 'base_url':'http://10.0.0.4:7574/solr'," +
" 'node_name':'node2'," +
" 'state':'active'}}}," +
" 'shard2':{" +
" 'range':'0-7fffffff'," +
" 'state':'active'," +
" 'replicas':{" +
" 'core_node2':{" +
" 'core':'gettingstarted_shard2_replica1'," +
" 'r3':{" +
" 'core':r3," +
" 'base_url':'http://10.0.0.4:8983/solr'," +
" 'node_name':'node1'," +
" 'state':'active'," +
" 'leader':'true'}," +
" 'core_node3':{" +
" 'core':'gettingstarted_shard2_replica2'," +
" 'r4':{" +
" 'core':r4," +
" 'base_url':'http://10.0.0.4:8987/solr'," +
" 'node_name':'node4'," +
" 'state':'active'}," +
" 'r6':{" +
" 'core':r6," +
" 'base_url':'http://10.0.0.4:8989/solr'," +
" 'node_name':'node3'," +
" 'state':'active'}," +
" 'r5':{" +
" 'core':r5," +
" 'base_url':'http://10.0.0.4:7574/solr'," +
" 'node_name':'node1'," +
" 'state':'active'}}}}}}";
@ -95,7 +121,6 @@ public class TestRuleSorter extends SolrTestCaseJ4 {
Map<String,Object> result = new LinkedHashMap<>();
keys.stream().forEach(s -> result.put(s, nodeValues.get(node).get(s)));
return result;
}
@Override
@ -115,7 +140,6 @@ public class TestRuleSorter extends SolrTestCaseJ4 {
List<RuleSorter.ReplicaStat> replicaStats = shardVsReplicaStats.get(shard);
if (replicaStats == null) shardVsReplicaStats.put(shard, replicaStats = new ArrayList<>());
replicaStats.add(new RuleSorter.ReplicaStat(replicaName, new HashMap<>()));
});
});
});
@ -137,7 +161,23 @@ public class TestRuleSorter extends SolrTestCaseJ4 {
System.out.printf(Utils.toJSONString(Utils.getDeepCopy(session.toMap(new LinkedHashMap<>()), 8)));
System.out.println(Utils.getDeepCopy(session.getViolations(), 6));
Map<String, List<Clause>> violations = session.getViolations();
System.out.println(Utils.getDeepCopy(violations, 6));
assertEquals(3, violations.size());
List<Clause> v = violations.get("node4");
assertNotNull(v);
assertEquals(v.get(0).tag.name, "nodeRole");
v = violations.get("node1");
assertNotNull(v);
assertEquals(v.get(0).replica.op, Operand.LESS_THAN);
assertEquals(v.get(0).replica.val, 2);
v = violations.get("node3");
assertNotNull(v);
assertEquals(v.get(0).replica.op, Operand.LESS_THAN);
assertEquals(v.get(0).replica.val, 1);
assertEquals(v.get(0).tag.val, "node3");
}