blob: e7364a81ab2f4d57e905c505b87e3801167f7967 [file] [log] [blame]
/*
* 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.client.solrj.cloud.autoscaling;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import org.apache.solr.common.cloud.Replica;
import org.apache.solr.common.util.Pair;
import static org.apache.solr.common.params.CollectionParams.CollectionAction.ADDREPLICA;
import static org.apache.solr.common.params.CollectionParams.CollectionAction.MOVEREPLICA;
/**
* Implements the 'withCollection' variable type
*
* @deprecated to be removed in Solr 9.0 (see SOLR-14656)
*/
public class WithCollectionVariable extends VariableBase {
public WithCollectionVariable(Type type) {
super(type);
}
@Override
public boolean match(Object inputVal, Operand op, Object val, String name, Row row) {
@SuppressWarnings({"unchecked"})
Map<String, String> withCollectionMap = (Map<String, String>) inputVal;
if (withCollectionMap == null || withCollectionMap.isEmpty()) return true;
Set<String> uniqueColls = new HashSet<>();
row.forEachReplica(replicaInfo -> uniqueColls.add(replicaInfo.getCollection()));
for (Map.Entry<String, String> e : withCollectionMap.entrySet()) {
if (uniqueColls.contains(e.getKey()) && !uniqueColls.contains(e.getValue())) return false;
}
return true;
}
public void projectAddReplica(Cell cell, ReplicaInfo ri, Consumer<Row.OperationInfo> opCollector, boolean strictMode) {
if (strictMode) {
// we do not want to add a replica of the 'withCollection' in strict mode
return;
}
@SuppressWarnings({"unchecked"})
Map<String, String> withCollectionMap = (Map<String, String>) cell.val;
if (withCollectionMap == null || withCollectionMap.isEmpty()) return;
Set<String> uniqueColls = new HashSet<>();
Row row = cell.row;
row.forEachReplica(replicaInfo -> uniqueColls.add(replicaInfo.getCollection()));
for (Map.Entry<String, String> e : withCollectionMap.entrySet()) {
if (uniqueColls.contains(e.getKey()) && !uniqueColls.contains(e.getValue())) {
String withCollection = e.getValue();
opCollector.accept(new Row.OperationInfo(withCollection, "shard1", row.node, cell.name, true, Replica.Type.NRT));
}
}
}
@Override
public int compareViolation(Violation v1, Violation v2) {
return Integer.compare(v1.getViolatingReplicas().size(), v2.getViolatingReplicas().size());
}
public boolean addViolatingReplicas(Violation.Ctx ctx) {
String node = ctx.currentViolation.node;
for (Row row : ctx.allRows) {
if (node.equals(row.node)) {
@SuppressWarnings({"unchecked"})
Map<String, String> withCollectionMap = (Map<String, String>) row.getVal("withCollection");
if (withCollectionMap != null) {
row.forEachReplica(r -> {
String withCollection = withCollectionMap.get(r.getCollection());
if (withCollection != null) {
// test whether this row has at least 1 replica of withCollection, else there is a violation
Set<String> uniqueCollections = new HashSet<>();
row.forEachReplica(replicaInfo -> uniqueCollections.add(replicaInfo.getCollection()));
if (!uniqueCollections.contains(withCollection)) {
ctx.currentViolation.addReplica(new Violation.ReplicaInfoAndErr(r).withDelta(1.0d));
}
}
});
ctx.currentViolation.replicaCountDelta = (double) ctx.currentViolation.getViolatingReplicas().size();
}
}
}
return true;
}
@Override
public void getSuggestions(Suggestion.Ctx ctx) {
if (ctx.violation.getViolatingReplicas().isEmpty()) return;
Map<String, Object> nodeValues = ctx.session.nodeStateProvider.getNodeValues(ctx.violation.node, Collections.singleton("withCollection"));
@SuppressWarnings({"unchecked"})
Map<String, String> withCollectionsMap = (Map<String, String>) nodeValues.get("withCollection");
if (withCollectionsMap == null) return;
Set<String> uniqueCollections = new HashSet<>();
for (Violation.ReplicaInfoAndErr replicaInfoAndErr : ctx.violation.getViolatingReplicas()) {
uniqueCollections.add(replicaInfoAndErr.replicaInfo.getCollection());
}
collectionLoop:
for (String collection : uniqueCollections) {
String withCollection = withCollectionsMap.get(collection);
if (withCollection == null) continue;
// can we find a node from which we can move a replica of the `withCollection`
// without creating another violation?
for (Row row : ctx.session.matrix) {
if (ctx.violation.node.equals(row.node)) continue; // filter the violating node
Set<String> hostedCollections = new HashSet<>();
row.forEachReplica(replicaInfo -> hostedCollections.add(replicaInfo.getCollection()));
if (hostedCollections.contains(withCollection) && !hostedCollections.contains(collection)) {
// find the candidate replicas that we can move
List<ReplicaInfo> movableReplicas = new ArrayList<>();
row.forEachReplica(replicaInfo -> {
if (replicaInfo.getCollection().equals(withCollection)) {
movableReplicas.add(replicaInfo);
}
});
for (ReplicaInfo toMove : movableReplicas) {
// candidate source node for a move replica operation
Suggester suggester = ctx.session.getSuggester(MOVEREPLICA)
.forceOperation(true)
.hint(Suggester.Hint.COLL_SHARD, new Pair<>(withCollection, "shard1"))
.hint(Suggester.Hint.SRC_NODE, row.node)
.hint(Suggester.Hint.REPLICA, toMove.getName())
.hint(Suggester.Hint.TARGET_NODE, ctx.violation.node);
if (ctx.addSuggestion(suggester) != null)
continue collectionLoop; // one suggestion is enough for this collection
}
}
}
// we could not find a valid move, so we suggest adding a replica
Suggester suggester = ctx.session.getSuggester(ADDREPLICA)
.forceOperation(true)
.hint(Suggester.Hint.COLL_SHARD, new Pair<>(withCollection, "shard1"))
.hint(Suggester.Hint.TARGET_NODE, ctx.violation.node);
ctx.addSuggestion(suggester);
}
}
}