SOLR-12618: AutoScalingHandlerTest failing in jenkins

This commit is contained in:
Noble Paul 2018-08-09 01:24:03 +10:00
parent 2674c53809
commit 9b418a4593
19 changed files with 462 additions and 73 deletions

View File

@ -23,6 +23,7 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrRequest;
@ -675,7 +676,6 @@ public class AutoScalingHandlerTest extends SolrCloudTestCase {
}
@Test
@BadApple(bugUrl="https://issues.apache.org/jira/browse/SOLR-12028")
public void testReadApi() throws Exception {
CloudSolrClient solrClient = cluster.getSolrClient();
// first trigger
@ -693,9 +693,7 @@ public class AutoScalingHandlerTest extends SolrCloudTestCase {
String setClusterPolicyCommand = "{" +
" 'set-cluster-policy': [" +
" {'cores':'<10', 'node':'#ANY'}," +
" {'replica':'<2', 'shard': '#EACH', 'node': '#ANY'}," +
" {'nodeRole':'overseer', 'replica':0}" +
" ]" +
" {'replica':'<3', 'shard': '#EACH', 'node': '#ANY'}]" +
"}";
req = createAutoScalingRequest(SolrRequest.METHOD.POST, setClusterPolicyCommand);
response = solrClient.request(req);
@ -713,17 +711,9 @@ public class AutoScalingHandlerTest extends SolrCloudTestCase {
assertEquals(response.get("result").toString(), "success");
String setPolicyCommand = "{'set-policy': {" +
" 'xyz':[" +
" {'replica':'<2', 'shard': '#EACH', 'node': '#ANY'}," +
" {'nodeRole':'overseer', 'replica':0}" +
" ]," +
" 'policy1':[" +
" {'replica':'<2', 'shard': '#EACH', 'node': '#ANY'}" +
" ]," +
" 'policy2':[" +
" {'replica':'<7', 'shard': '#EACH', 'node': '#ANY'}" +
" ]" +
"}}";
" 'xyz':[{'replica':'<2', 'shard': '#EACH', 'node': '#ANY'}]," +
" 'policy1':[{'replica':'<2', 'shard': '#EACH', 'node': '#ANY'}]," +
" 'policy2':[{'replica':'<7', 'shard': '#EACH', 'node': '#ANY'}]}}";
req = createAutoScalingRequest(SolrRequest.METHOD.POST, setPolicyCommand);
response = solrClient.request(req);
assertEquals(response.get("result").toString(), "success");
@ -747,7 +737,7 @@ public class AutoScalingHandlerTest extends SolrCloudTestCase {
List<Map> clusterPolicy = (List<Map>) response.get("cluster-policy");
assertNotNull(clusterPolicy);
assertEquals(3, clusterPolicy.size());
assertEquals(2, clusterPolicy.size());
Map policies = (Map) response.get("policies");
assertNotNull(policies);
@ -766,11 +756,11 @@ public class AutoScalingHandlerTest extends SolrCloudTestCase {
for (int i = 0; i < 2; i++) {
Map node = (Map) sortedNodes.get(i);
assertNotNull(node);
assertEquals(6, node.size());
assertNotNull(node.get("node"));
assertNotNull(node.get("cores"));
assertEquals(0d, node.get("cores"));
assertNotNull(node.get("freedisk"));
assertNotNull(node.get("replicas"));
assertTrue(node.get("freedisk") instanceof Double);
assertNotNull(node.get("sysLoadAvg"));
assertTrue(node.get("sysLoadAvg") instanceof Double);
@ -800,8 +790,7 @@ public class AutoScalingHandlerTest extends SolrCloudTestCase {
String tempClusterPolicyCommand = "{" +
" 'set-cluster-policy': [" +
" {'cores':'<10', 'node':'#ANY'}," +
" {'replica':'<4', 'shard': '#EACH', 'node': '#ANY'}," +
" {'nodeRole':'overseer', 'replica':0}" +
" {'replica':'<4', 'shard': '#EACH', 'node': '#ANY'}"+
" ]" +
"}";
req = createAutoScalingRequest(SolrRequest.METHOD.POST, tempClusterPolicyCommand);
@ -831,12 +820,26 @@ public class AutoScalingHandlerTest extends SolrCloudTestCase {
for (Map<String, Object> violation : violations) {
assertEquals("readApiTestViolations", violation.get("collection"));
assertEquals("shard1", violation.get("shard"));
assertEquals(2d, getObjectByPath(violation, true, "violation/delta"));
assertEquals(1.0d, getObjectByPath(violation, true, "violation/delta"));
assertEquals(3l, getObjectByPath(violation, true, "violation/replica/NRT"));
assertNotNull(violation.get("clause"));
}
log.info("Before starting new jetty ,{}", cluster.getJettySolrRunners()
.stream()
.map(jettySolrRunner -> jettySolrRunner.getNodeName()).collect(Collectors.toList()));
JettySolrRunner runner1 = cluster.startJettySolrRunner();
cluster.waitForAllNodes(30);
log.info("started new jetty {}", runner1.getNodeName());
response = waitForResponse(namedList -> {
List l = (List) Utils.getObjectByPath(namedList, false, "diagnostics/liveNodes");
if (l != null && l.contains(runner1.getNodeName())) return true;
return false;
},
createAutoScalingRequest(SolrRequest.METHOD.GET, "/diagnostics", null),
200,
20,
runner1.getNodeName() + " could not come up ");
req = createAutoScalingRequest(SolrRequest.METHOD.GET, "/suggestions", null);
response = solrClient.request(req);

View File

@ -47,16 +47,19 @@ class AddReplicaSuggester extends Suggester {
//iterate through nodes and identify the least loaded
List<Violation> leastSeriousViolation = null;
Row bestNode = null;
double[] bestDeviation = null;
for (int i = getMatrix().size() - 1; i >= 0; i--) {
Row row = getMatrix().get(i);
if (!isNodeSuitableForReplicaAddition(row)) continue;
Row tmpRow = row.addReplica(shard.first(), shard.second(), type, strict);
List<Violation> errs = testChangedMatrix(strict, tmpRow.session);
double[] deviation = new double[1];
List<Violation> errs = testChangedMatrix(strict, tmpRow.session, deviation);
if (!containsNewErrors(errs)) {
if (isLessSerious(errs, leastSeriousViolation)) {
if ((errs.isEmpty() && isLessDeviant(bestDeviation, deviation)) ||//there are no violations but this is deviating less
isLessSerious(errs, leastSeriousViolation)) {//there are errors , but this has less serious violation
leastSeriousViolation = errs;
bestNode = tmpRow;
bestDeviation = deviation;
}
}
}
@ -73,6 +76,7 @@ class AddReplicaSuggester extends Suggester {
return null;
}
@Override
public CollectionParams.CollectionAction getAction() {
return ADDREPLICA;

View File

@ -42,6 +42,7 @@ import static org.apache.solr.client.solrj.cloud.autoscaling.Operand.EQUAL;
import static org.apache.solr.client.solrj.cloud.autoscaling.Operand.GREATER_THAN;
import static org.apache.solr.client.solrj.cloud.autoscaling.Operand.LESS_THAN;
import static org.apache.solr.client.solrj.cloud.autoscaling.Operand.NOT_EQUAL;
import static org.apache.solr.client.solrj.cloud.autoscaling.Operand.RANGE_EQUAL;
import static org.apache.solr.client.solrj.cloud.autoscaling.Operand.WILDCARD;
import static org.apache.solr.client.solrj.cloud.autoscaling.Policy.ANY;
import static org.apache.solr.common.params.CoreAdminParams.COLLECTION;
@ -215,7 +216,9 @@ public class Clause implements MapWriter, Comparable<Clause> {
if (this.isPerCollectiontag() && that.isPerCollectiontag()) {
v = Integer.compare(this.replica.op.priority, that.replica.op.priority);
if (v == 0) {// higher the number of replicas , harder to satisfy
v = Preference.compareWithTolerance((Double) this.replica.val, (Double) that.replica.val, 1);
Double thisVal = this.replica.val instanceof RangeVal ? ((RangeVal) this.replica.val).max.doubleValue() : (Double) this.replica.val;
Double thatVal = that.replica.val instanceof RangeVal ? ((RangeVal) that.replica.val).max.doubleValue() : (Double) that.replica.val;
v = Preference.compareWithTolerance(thisVal, thatVal, 1);
v = this.replica.op == LESS_THAN ? v : v * -1;
}
if (v == 0) v = compareTypes(this.type, that.type);
@ -240,7 +243,7 @@ public class Clause implements MapWriter, Comparable<Clause> {
//replica value is zero
boolean isReplicaZero() {
return replica != null && replica.getOperand() == Operand.EQUAL &&
return replica != null && replica.getOperand() == Operand.EQUAL && replica.val instanceof Double &&
Preference.compareWithTolerance(0d, (Double) replica.val, 1) == 0;
}
@ -353,7 +356,7 @@ public class Clause implements MapWriter, Comparable<Clause> {
return operand;
}
public List<Violation> test(Policy.Session session) {
public List<Violation> test(Policy.Session session, double[] deviations) {
ComputedValueEvaluator computedValueEvaluator = new ComputedValueEvaluator(session);
Violation.Ctx ctx = new Violation.Ctx(this, session.matrix, computedValueEvaluator);
if (isPerCollectiontag()) {
@ -377,6 +380,13 @@ public class Clause implements MapWriter, Comparable<Clause> {
sealedClause.getReplica().delta(replicas),
tag.varType.meta.isNodeSpecificVal() ? null : counts.getKey());
tag.varType.addViolatingReplicas(ctx.reset(counts.getKey(), replicas, violation));
} else {
if (deviations != null && sealedClause.replica.op == RANGE_EQUAL) {
Number actualCount = replicas.getVal(type);
Double realDelta = ((RangeVal) sealedClause.replica.val).realDelta(actualCount.doubleValue());
realDelta = this.isReplicaZero() ? -1 * realDelta : realDelta;
deviations[0] += Math.abs(realDelta);
}
}
}
}
@ -422,7 +432,9 @@ public class Clause implements MapWriter, Comparable<Clause> {
tagVsCount.get(row.node).increment(shards.getValue());
}
} else {
tagVsCount.computeIfAbsent(String.valueOf(t.getValue()), s -> new ReplicaCount());
boolean pass = sealedClause.getTag().isPass(tagVal);
if(!pass && !isReplicaZero()) continue;
tagVsCount.computeIfAbsent(pass ? String.valueOf(tagVal) : "", s -> new ReplicaCount());
if (pass) {
tagVsCount.get(String.valueOf(tagVal)).increment(shards.getValue());

View File

@ -62,7 +62,8 @@ public class MoveReplicaSuggester extends Suggester {
if (!isNodeSuitableForReplicaAddition(targetRow)) continue;
targetRow = targetRow.addReplica(ri.getCollection(), ri.getShard(), ri.getType(), strict); // add replica to target first
Row srcRowModified = targetRow.session.getNode(fromRow.node).removeReplica(ri.getCollection(), ri.getShard(), ri.getType());//then remove replica from source node
List<Violation> errs = testChangedMatrix(strict, srcRowModified.session);
double[] deviation = new double[1];
List<Violation> errs = testChangedMatrix(strict, srcRowModified.session, deviation);
srcRowModified.session.applyRules(); // now resort the nodes with the new values
Policy.Session tmpSession = srcRowModified.session;

View File

@ -33,13 +33,12 @@ public class NodeVariable extends VariableBase {
if (ctx.violation.replicaCountDelta > 0) {//there are more replicas than necessary
for (int i = 0; i < Math.abs(ctx.violation.replicaCountDelta); i++) {
Suggester suggester = ctx.session.getSuggester(MOVEREPLICA)
.forceOperation(true)
.hint(Suggester.Hint.SRC_NODE, ctx.violation.node)
.hint(ctx.violation.shard.equals(ANY) ? Suggester.Hint.COLL : Suggester.Hint.COLL_SHARD,
ctx.violation.shard.equals(ANY) ? ctx.violation.coll : new Pair<>(ctx.violation.coll, ctx.violation.shard));
ctx.addSuggestion(suggester);
if(ctx.addSuggestion(suggester) == null) break;
}
}
}
}

View File

@ -572,7 +572,7 @@ public class Policy implements MapWriter {
setApproxValuesAndSortNodes(clusterPreferences, matrix);
for (Clause clause : expandedClauses) {
List<Violation> errs = clause.test(this);
List<Violation> errs = clause.test(this, null);
violations.addAll(errs);
}
}

View File

@ -28,12 +28,14 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Predicate;
import org.apache.solr.client.solrj.SolrRequest;
import org.apache.solr.client.solrj.cloud.DistribStateManager;
import org.apache.solr.client.solrj.cloud.SolrCloudManager;
import org.apache.solr.client.solrj.cloud.autoscaling.Suggester.Hint;
import org.apache.solr.client.solrj.impl.ClusterStateProvider;
import org.apache.solr.common.IteratorWriter;
import org.apache.solr.common.MapWriter;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.cloud.DocCollection;
@ -183,30 +185,20 @@ public class PolicyHelper {
public static MapWriter getDiagnostics(Policy policy, SolrCloudManager cloudManager) {
Policy.Session session = policy.createSession(cloudManager);
List<Row> sorted = session.getSortedNodes();
List<Violation> violations = session.getViolations();
List<Preference> clusterPreferences = policy.getClusterPreferences();
List<Map<String, Object>> sortedNodes = new ArrayList<>(sorted.size());
for (Row row : sorted) {
Map<String, Object> map = Utils.makeMap("node", row.node);
map.put("isLive", row.isLive);
for (Cell cell : row.getCells()) {
for (Preference clusterPreference : clusterPreferences) {
Policy.SortParam name = clusterPreference.getName();
if (cell.getName().equalsIgnoreCase(name.name())) {
map.put(name.name(), cell.getValue());
break;
}
}
return ew -> ew.put("sortedNodes", (IteratorWriter) iw -> {
for (Row row : sorted) {
iw.add((MapWriter) ew1 -> {
ew1.put("node", row.node).
put("isLive", row.isLive);
for (Cell cell : row.getCells())
ew1.put(cell.name, cell.val,
(Predicate) o -> o != null && (!(o instanceof Map) || !((Map) o).isEmpty()));
ew1.put("replicas", row.collectionVsShardVsReplicas);
});
}
sortedNodes.add(map);
}
return ew -> {
ew.put("sortedNodes", sortedNodes);
ew.put("violations", violations);
};
}).put("liveNodes", cloudManager.getClusterStateProvider().getLiveNodes())
.put("violations", session.getViolations())
.put("config", session.getPolicy());
}

View File

@ -35,8 +35,12 @@ class RangeVal implements MapWriter {
Double.compare(testVal.doubleValue(), max.doubleValue()) <= 0;
}
public Double realDelta(double v) {
if (actual != null) return v - actual.doubleValue();
else return delta(v);
}
public Double delta(double v) {
// if (actual != null) return v - actual.doubleValue();
if (v >= max.doubleValue()) return v - max.doubleValue();
if (v <= min.doubleValue()) return v - min.doubleValue();
return 0d;
@ -49,6 +53,8 @@ class RangeVal implements MapWriter {
@Override
public void writeMap(EntryWriter ew) throws IOException {
ew.put("min", min).put("max", max).putIfNotNull("actual", actual);
ew.put("min", min)
.put("max", max)
.putIfNotNull("actual", actual);
}
}

View File

@ -33,9 +33,9 @@ class ReplicaCount implements MapWriter {
@Override
public void writeMap(EntryWriter ew) throws IOException {
ew.put(Replica.Type.NRT.name(), nrt);
ew.put(Replica.Type.PULL.name(), pull);
ew.put(Replica.Type.TLOG.name(), tlog);
if (nrt > 0) ew.put(Replica.Type.NRT.name(), nrt);
if (pull > 0) ew.put(Replica.Type.PULL.name(), pull);
if (tlog > 0) ew.put(Replica.Type.TLOG.name(), tlog);
ew.put("count", total());
}

View File

@ -53,7 +53,7 @@ public class ReplicaInfo implements MapWriter {
}
public ReplicaInfo(String name, String core, String coll, String shard, Replica.Type type, String node, Map<String, Object> vals) {
if(vals==null) vals = Collections.emptyMap();
if (vals == null) vals = Collections.emptyMap();
this.name = name;
if (vals != null) {
this.variables.putAll(vals);
@ -66,6 +66,21 @@ public class ReplicaInfo implements MapWriter {
this.node = node;
}
ReplicaInfo(Map<String, Object> map) {
this.name = map.keySet().iterator().next();
Map details = (Map) map.get(name);
details = Utils.getDeepCopy(details, 4);
this.collection = (String) details.remove("collection");
this.shard = (String) details.remove("shard");
this.core = (String) details.remove("core");
this.node = (String) details.remove("node_name");
this.isLeader = Boolean.parseBoolean((String) details.getOrDefault("leader", "false"));
details.remove("leader");
type = Replica.Type.valueOf((String) details.getOrDefault("type", "NRT"));
details.remove("type");
this.variables.putAll(details);
}
public Object clone() {
return new ReplicaInfo(name, core, collection, shard, type, node, variables);
}

View File

@ -35,6 +35,7 @@ import java.util.function.Predicate;
import org.apache.solr.client.solrj.SolrRequest;
import org.apache.solr.client.solrj.impl.ClusterStateProvider;
import org.apache.solr.common.ConditionalMapWriter;
import org.apache.solr.common.MapWriter;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.cloud.DocCollection;
@ -70,6 +71,10 @@ public abstract class Suggester implements MapWriter {
this.session = session.copy();
}
boolean isLessDeviant(double[] previousBest, double[] newDeviation) {
if (previousBest == null) return true;
return newDeviation[0] < previousBest[0];
}
public Suggester hint(Hint hint, Object value) {
hint.validator.accept(value);
if (hint.multiValued) {
@ -203,7 +208,9 @@ public abstract class Suggester implements MapWriter {
@Override
public void writeMap(EntryWriter ew) throws IOException {
ew.put("type", violation == null ? "improvement" : "violation");
ew.putIfNotNull("violation", violation);
ew.putIfNotNull("violation",
new ConditionalMapWriter(violation,
(k, v) -> !"violatingReplicas".equals(k)));
ew.put("operation", operation);
}
@ -237,6 +244,7 @@ public abstract class Suggester implements MapWriter {
boolean containsNewErrors(List<Violation> violations) {
boolean isTxOpen = session.transaction != null && session.transaction.isOpen();
if (violations.size() > originalViolations.size()) return true;
for (Violation v : violations) {
//the computed value can change over time. So it's better to evaluate it in the end
if (isTxOpen && v.getClause().hasComputedValue) continue;
@ -272,12 +280,12 @@ public abstract class Suggester implements MapWriter {
}
}
List<Violation> testChangedMatrix(boolean strict, Policy.Session session) {
List<Violation> testChangedMatrix(boolean strict, Policy.Session session, double[] deviation) {
Policy.setApproxValuesAndSortNodes(session.getPolicy().clusterPreferences, session.matrix);
List<Violation> errors = new ArrayList<>();
for (Clause clause : session.expandedClauses) {
if (strict || clause.strict) {
List<Violation> errs = clause.test(session);
List<Violation> errs = clause.test(session, deviation);
if (!errs.isEmpty()) {
errors.addAll(errs);
}

View File

@ -107,6 +107,7 @@ public class VariableBase implements Variable {
val = condition.op.readRuleValue(condition);
if (val != condition.val) return val;
}
if (!isRuleVal && "".equals(val) && this.varType.type != String.class) val = -1;
if (name == null) name = this.varType.tagName;
if (varType.type == Double.class) {
Double num = Clause.parseDouble(name, val);

View File

@ -25,6 +25,7 @@ import java.util.List;
import java.util.Objects;
import java.util.function.Function;
import org.apache.solr.common.IteratorWriter;
import org.apache.solr.common.MapWriter;
import org.apache.solr.common.util.Utils;
@ -160,13 +161,20 @@ public class Violation implements MapWriter {
ew.putIfNotNull("collection", coll);
ew.putIfNotNull("shard", shard);
ew.putIfNotNull("node", node);
ew.putIfNotNull("tagKey", String.valueOf(tagKey));
ew.putStringIfNotNull("tagKey", tagKey);
ew.putIfNotNull("violation", (MapWriter) ew1 -> {
if (getClause().isPerCollectiontag()) ew1.put("replica", actualVal);
else ew1.put(clause.tag.name, String.valueOf(actualVal));
ew1.putIfNotNull("delta", replicaCountDelta);
});
ew.put("clause", getClause());
if (!replicaInfoAndErrs.isEmpty()) {
ew.put("violatingReplicas", (IteratorWriter) iw -> {
for (ReplicaInfoAndErr replicaInfoAndErr : replicaInfoAndErrs) {
iw.add(replicaInfoAndErr.replicaInfo);
}
});
}
}
static class Ctx {

View File

@ -94,7 +94,7 @@ public class SolrClientNodeStateProvider implements NodeStateProvider, MapWriter
return solrClient.getClusterStateProvider();
}
private void readReplicaDetails() throws IOException {
protected void readReplicaDetails() throws IOException {
ClusterStateProvider clusterStateProvider = getClusterStateProvider();
ClusterState clusterState = clusterStateProvider.getClusterState();
if (clusterState == null) { // zkStateReader still initializing

View File

@ -0,0 +1,75 @@
/*
* 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.common;
import java.io.IOException;
import java.util.function.BiPredicate;
public class ConditionalMapWriter implements MapWriter {
private final MapWriter delegate;
private final BiPredicate<String, Object> predicate;
public ConditionalMapWriter(MapWriter delegate, BiPredicate<String, Object> predicate) {
this.delegate = delegate;
this.predicate = predicate;
}
private class EntryWriterWrapper implements EntryWriter {
private final EntryWriter delegate;
EntryWriterWrapper(EntryWriter delegate) {
this.delegate = delegate;
}
@Override
public EntryWriter put(String k, Object v) throws IOException {
if (predicate.test(k, v)) delegate.put(k, v);
return this;
}
@Override
public EntryWriter put(String k, int v) throws IOException {
return put(k, Integer.valueOf(v));
}
@Override
public EntryWriter put(String k, long v) throws IOException {
return put(k, Long.valueOf(v));
}
@Override
public EntryWriter put(String k, float v) throws IOException {
return put(k, Float.valueOf(v));
}
@Override
public EntryWriter put(String k, double v) throws IOException {
return put(k, Double.valueOf(v));
}
@Override
public EntryWriter put(String k, boolean v) throws IOException {
return put(k, Boolean.valueOf(v));
}
}
@Override
public void writeMap(EntryWriter ew) throws IOException {
delegate.writeMap(new EntryWriterWrapper(ew));
}
}

View File

@ -23,6 +23,7 @@ import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
import org.apache.solr.common.util.Utils;
@ -102,6 +103,11 @@ public interface MapWriter extends MapSerializable {
return this;
}
default EntryWriter put(String k, Object v, Predicate<Object> p) throws IOException {
if (p.test(v)) put(k, v);
return this;
}
default EntryWriter putIfNotNull(String k, Object v) throws IOException {
if(v != null) put(k,v);
return this;

View File

@ -2467,8 +2467,8 @@ public class TestPolicy extends SolrTestCaseJ4 {
" }}}";
String autoScalingjson = " { cluster-policy:[" +
" { replica :'<34%', shard: '#EACH', sysprop.az : east}," +
" { replica :'<67%', shard: '#EACH', sysprop.az : west}" +
" { replica :'33%', shard: '#EACH', sysprop.az : east}," +
" { replica :'67%', shard: '#EACH', sysprop.az : west}" +
" ]," +
" cluster-preferences :[{ minimize : cores }]}";
@ -2480,6 +2480,7 @@ public class TestPolicy extends SolrTestCaseJ4 {
List<String> nodes = new ArrayList<>();
int westCount = 0, eastCount = 0;
for (int i = 0; i < 12; i++) {
SolrRequest suggestion = txn.getCurrentSession()
.getSuggester(ADDREPLICA)
@ -2488,9 +2489,13 @@ public class TestPolicy extends SolrTestCaseJ4 {
assertNotNull(suggestion);
String node = suggestion.getParams().get("node");
nodes.add(node);
if ("10.0.0.6:8983_solr".equals(node)) eastCount++;
if ("10.0.0.6:7574_solr".equals(node)) westCount++;
if (i % 3 == 1) assertEquals("10.0.0.6:8983_solr", node);
else assertEquals("10.0.0.6:7574_solr", node);
}
assertEquals(8, westCount);
assertEquals(4, eastCount);
List<Violation> violations = txn.close();
assertTrue(violations.isEmpty());
@ -2655,7 +2660,7 @@ public class TestPolicy extends SolrTestCaseJ4 {
"}";
AutoScalingConfig cfg = new AutoScalingConfig((Map<String, Object>) Utils.fromJSONString(autoScalingjson));
List<Violation> violations = cfg.getPolicy().createSession(cloudManagerWithData(dataproviderdata)).getViolations();
assertEquals(2, violations.size());
assertEquals("expected 2 violations", 2, violations.size());
List<Suggester.SuggestionInfo> suggestions = PolicyHelper.getSuggestions(cfg, cloudManagerWithData(dataproviderdata));
assertEquals(2, suggestions.size());
for (Suggester.SuggestionInfo suggestion : suggestions) {
@ -3562,11 +3567,6 @@ public class TestPolicy extends SolrTestCaseJ4 {
Object root = Utils.fromJSONString(writer.toString());
assertEquals(2l,
Utils.getObjectByPath(root, true, "violations[0]/violation/replica/NRT"));
assertEquals(0l,
Utils.getObjectByPath(root, true, "violations[0]/violation/replica/PULL"));
assertEquals(0l,
Utils.getObjectByPath(root, true, "violations[0]/violation/replica/TLOG"));
}

View File

@ -19,6 +19,7 @@ package org.apache.solr.client.solrj.cloud.autoscaling;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
@ -288,4 +289,243 @@ public class TestPolicy2 extends SolrTestCaseJ4 {
};
}
public void testAutoScalingHandlerFailure() {
String diagnostics = "{" +
" 'diagnostics': {" +
" 'sortedNodes': [" +
" {" +
" 'node': '127.0.0.1:63191_solr'," +
" 'isLive': true," +
" 'cores': 3.0," +
" 'freedisk': 680.908073425293," +
" 'heapUsage': 24.97510064011647," +
" 'sysLoadAvg': 272.75390625," +
" 'totaldisk': 1037.938980102539," +
" 'replicas': {" +
" 'readApiTestViolations': {" +
" 'shard1': [" +
" {" +
" 'core_node5': {" +
" 'core': 'readApiTestViolations_shard1_replica_n2'," +
" 'leader': 'true'," +
" 'base_url': 'https://127.0.0.1:63191/solr'," +
" 'node_name': '127.0.0.1:63191_solr'," +
" 'state': 'active'," +
" 'type': 'NRT'," +
" 'force_set_state': 'false'," +
" 'INDEX.sizeInGB': 6.426125764846802E-8," +
" 'shard': 'shard1'," +
" 'collection': 'readApiTestViolations'" +
" }" +
" }," +
" {" +
" 'core_node7': {" +
" 'core': 'readApiTestViolations_shard1_replica_n4'," +
" 'base_url': 'https://127.0.0.1:63191/solr'," +
" 'node_name': '127.0.0.1:63191_solr'," +
" 'state': 'active'," +
" 'type': 'NRT'," +
" 'force_set_state': 'false'," +
" 'INDEX.sizeInGB': 6.426125764846802E-8," +
" 'shard': 'shard1'," +
" 'collection': 'readApiTestViolations'" +
" }" +
" }," +
" {" +
" 'core_node12': {" +
" 'core': 'readApiTestViolations_shard1_replica_n10'," +
" 'base_url': 'https://127.0.0.1:63191/solr'," +
" 'node_name': '127.0.0.1:63191_solr'," +
" 'state': 'active'," +
" 'type': 'NRT'," +
" 'force_set_state': 'false'," +
" 'INDEX.sizeInGB': 6.426125764846802E-8," +
" 'shard': 'shard1'," +
" 'collection': 'readApiTestViolations'" +
" }" +
" }" +
" ]" +
" }" +
" }" +
" }," +
" {" +
" 'node': '127.0.0.1:63192_solr'," +
" 'isLive': true," +
" 'cores': 3.0," +
" 'freedisk': 680.908073425293," +
" 'heapUsage': 24.98878807983566," +
" 'sysLoadAvg': 272.75390625," +
" 'totaldisk': 1037.938980102539," +
" 'replicas': {" +
" 'readApiTestViolations': {" +
" 'shard1': [" +
" {" +
" 'core_node3': {" +
" 'core': 'readApiTestViolations_shard1_replica_n1'," +
" 'base_url': 'https://127.0.0.1:63192/solr'," +
" 'node_name': '127.0.0.1:63192_solr'," +
" 'state': 'active'," +
" 'type': 'NRT'," +
" 'force_set_state': 'false'," +
" 'INDEX.sizeInGB': 6.426125764846802E-8," +
" 'shard': 'shard1'," +
" 'collection': 'readApiTestViolations'" +
" }" +
" }," +
" {" +
" 'core_node9': {" +
" 'core': 'readApiTestViolations_shard1_replica_n6'," +
" 'base_url': 'https://127.0.0.1:63192/solr'," +
" 'node_name': '127.0.0.1:63192_solr'," +
" 'state': 'active'," +
" 'type': 'NRT'," +
" 'force_set_state': 'false'," +
" 'INDEX.sizeInGB': 6.426125764846802E-8," +
" 'shard': 'shard1'," +
" 'collection': 'readApiTestViolations'" +
" }" +
" }," +
" {" +
" 'core_node11': {" +
" 'core': 'readApiTestViolations_shard1_replica_n8'," +
" 'base_url': 'https://127.0.0.1:63192/solr'," +
" 'node_name': '127.0.0.1:63192_solr'," +
" 'state': 'active'," +
" 'type': 'NRT'," +
" 'force_set_state': 'false'," +
" 'INDEX.sizeInGB': 6.426125764846802E-8," +
" 'shard': 'shard1'," +
" 'collection': 'readApiTestViolations'" +
" }" +
" }" +
" ]" +
" }" +
" }" +
" }," +
" {" +
" 'node': '127.0.0.1:63219_solr'," +
" 'isLive': true," +
" 'cores': 0.0," +
" 'freedisk': 680.908073425293," +
" 'heapUsage': 24.98878807983566," +
" 'sysLoadAvg': 272.75390625," +
" 'totaldisk': 1037.938980102539," +
" 'replicas': {}" +
" }" +
" ]," +
" 'liveNodes': [" +
" '127.0.0.1:63191_solr'," +
" '127.0.0.1:63192_solr'," +
" '127.0.0.1:63219_solr'" +
" ]," +
" 'violations': [" +
" {" +
" 'collection': 'readApiTestViolations'," +
" 'shard': 'shard1'," +
" 'node': '127.0.0.1:63191_solr'," +
" 'violation': {" +
" 'replica': {'NRT': 3, 'count': 3}," +
" 'delta': 2.0" +
" }," +
" 'clause': {" +
" 'replica': '<3'," +
" 'shard': '#EACH'," +
" 'node': '#ANY'," +
" 'collection': 'readApiTestViolations'" +
" }" +
" }," +
" {" +
" 'collection': 'readApiTestViolations'," +
" 'shard': 'shard1'," +
" 'node': '127.0.0.1:63192_solr'," +
" 'violation': {" +
" 'replica': {'NRT': 3, 'count': 3}," +
" 'delta': 2.0" +
" }," +
" 'clause': {" +
" 'replica': '<2'," +
" 'shard': '#EACH'," +
" 'node': '#ANY'," +
" 'collection': 'readApiTestViolations'" +
" }" +
" }" +
" ]," +
" 'config': {" +
" 'cluster-preferences': [" +
" {'minimize': 'cores', 'precision': 3}," +
" {'maximize': 'freedisk', 'precision': 100}," +
" {'minimize': 'sysLoadAvg', 'precision': 10}," +
" {'minimize': 'heapUsage', 'precision': 10}" +
" ]," +
" 'cluster-policy': [" +
" {'cores': '<10', 'node': '#ANY'}," +
" {'replica': '<2', 'shard': '#EACH', 'node': '#ANY'}," +
" {'nodeRole': 'overseer', 'replica': 0}" +
" ]" +
" }" +
" }}";
Map<String, Object> m = (Map<String, Object>) Utils.fromJSONString(diagnostics);
Policy policy = new Policy((Map<String, Object>) Utils.getObjectByPath(m, false, "diagnostics/config"));
SolrCloudManager cloudManagerFromDiagnostics = createCloudManagerFromDiagnostics(m);
Policy.Session session = policy.createSession(cloudManagerFromDiagnostics);
List<Suggester.SuggestionInfo> suggestions = PolicyHelper.getSuggestions(new AutoScalingConfig((Map<String, Object>) Utils.getObjectByPath(m, false, "diagnostics/config")), cloudManagerFromDiagnostics);
assertEquals(2, suggestions.size());
}
static SolrCloudManager createCloudManagerFromDiagnostics(Map<String, Object> m) {
List<Map> sortedNodes = (List<Map>) Utils.getObjectByPath(m, false, "diagnostics/sortedNodes");
Set<String> liveNodes = new HashSet<>();
SolrClientNodeStateProvider nodeStateProvider = new SolrClientNodeStateProvider(null) {
@Override
protected void readReplicaDetails() {
for (Object o : sortedNodes) {
String node = (String) ((Map) o).get("node");
liveNodes.add(node);
Map nodeDetails = nodeVsCollectionVsShardVsReplicaInfo.computeIfAbsent(node, s -> new LinkedHashMap<>());
Map<String, Map<String, List<Map>>> replicas = (Map<String, Map<String, List<Map>>>) ((Map) o).get("replicas");
replicas.forEach((coll, shardVsReplicas) -> shardVsReplicas
.forEach((shard, repDetails) -> {
List<ReplicaInfo> reps = (List) ((Map) nodeDetails
.computeIfAbsent(coll, o1 -> new LinkedHashMap<>()))
.computeIfAbsent(shard, o12 -> new ArrayList<ReplicaInfo>());
for (Map map : repDetails) reps.add(new ReplicaInfo(map));
}));
}
}
@Override
public Map<String, Map<String, List<ReplicaInfo>>> getReplicaInfo(String node, Collection<String> keys) {
return nodeVsCollectionVsShardVsReplicaInfo.get(node) == null ?
Collections.emptyMap() :
nodeVsCollectionVsShardVsReplicaInfo.get(node);
}
@Override
public Map<String, Object> getNodeValues(String node, Collection<String> tags) {
for (Map n : sortedNodes) if (n.get("node").equals(node)) return n;
return Collections.emptyMap();
}
};
return new DelegatingCloudManager(null) {
@Override
public NodeStateProvider getNodeStateProvider() {
return nodeStateProvider;
}
@Override
public ClusterStateProvider getClusterStateProvider() {
return new DelegatingClusterStateProvider(null) {
@Override
public Set<String> getLiveNodes() {
return liveNodes;
}
};
}
};
}
}

View File

@ -34,6 +34,7 @@ import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Predicate;
import org.apache.solr.SolrTestCaseJ4;
import org.apache.solr.client.solrj.SolrRequest;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.embedded.JettyConfig;
import org.apache.solr.client.solrj.embedded.JettySolrRunner;
@ -49,6 +50,7 @@ import org.apache.solr.common.cloud.Replica;
import org.apache.solr.common.cloud.Slice;
import org.apache.solr.common.cloud.SolrZkClient;
import org.apache.solr.common.cloud.ZkStateReader;
import org.apache.solr.common.util.NamedList;
import org.junit.AfterClass;
import org.junit.Before;
@ -352,4 +354,21 @@ public class SolrCloudTestCase extends SolrTestCaseJ4 {
}
}
protected NamedList waitForResponse(Predicate<NamedList> predicate, SolrRequest request, int intervalInMillis, int numRetries, String messageOnFail) {
int i = 0;
for (; i < numRetries; i++) {
try {
NamedList<Object> response = cluster.getSolrClient().request(request);
if (predicate.test(response)) return response;
Thread.sleep(intervalInMillis);
} catch (RuntimeException rte) {
throw rte;
} catch (Exception e) {
throw new RuntimeException("error executing request", e);
}
}
fail("Tried " + i + " times , could not succeed. " + messageOnFail);
return null;
}
}