blob: 4f0c48081ad15f3db7860bed3604a01a77bea934 [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.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.apache.solr.common.util.StrUtils;
/**
*
* @deprecated to be removed in Solr 9.0 (see SOLR-14656)
*/
class ReplicaVariable extends VariableBase {
public ReplicaVariable(Type type) {
super(type);
}
public static final String REPLICASCOUNT = "relevantReplicas";
static int getRelevantReplicasCount(Policy.Session session, Condition cv, String collection, String shard) {
int totalReplicasOfInterest = 0;
Clause clause = cv.getClause();
for (Row row : session.matrix) {
Integer perShardCount = row.computeCacheIfAbsent(collection, shard, REPLICASCOUNT, cv.clause, o -> {
int[] result = new int[1];
row.forEachReplica(collection, replicaInfo -> {
if (clause.isMatch(replicaInfo, collection, shard))
result[0]++;
});
return result[0];
});
if (perShardCount != null)
totalReplicasOfInterest += perShardCount;
}
return totalReplicasOfInterest;
}
@Override
public Object validate(String name, Object val, boolean isRuleVal) {
return getOperandAdjustedValue(super.validate(name, val, isRuleVal), val);
}
@Override
public Operand getOperand(Operand expected, Object strVal, ComputedType computedType) {
if (computedType == ComputedType.ALL) return expected;
return checkForRangeOperand(expected, strVal, computedType);
}
static Operand checkForRangeOperand(Operand expected, Object strVal, ComputedType computedType) {
if (strVal instanceof String) {
String s = ((String) strVal).trim();
int hyphenIdx = s.indexOf('-');
if (hyphenIdx > 0) {
if (hyphenIdx == s.length() - 1) {
throw new IllegalArgumentException("bad range input :" + expected);
}
if (expected == Operand.EQUAL) return Operand.RANGE_EQUAL;
if (expected == Operand.NOT_EQUAL) return Operand.RANGE_NOT_EQUAL;
}
}
if (expected == Operand.EQUAL && (computedType != null || !isIntegerEquivalent(strVal))) {
return Operand.RANGE_EQUAL;
}
if (expected == Operand.NOT_EQUAL && (computedType != null || !isIntegerEquivalent(strVal)))
return Operand.RANGE_NOT_EQUAL;
return expected;
}
@Override
public String postValidate(Condition condition) {
Object val = condition.clause.getThirdTag().val;
boolean isNodesetObjectList = condition.clause.nodeSetPresent && (val instanceof List) && ((List)val).get(0) instanceof Condition ;
if(condition.clause.nodeSetPresent ){
if(condition.computedType == ComputedType.EQUAL){
if(!isNodesetObjectList) return " 'nodeset' must have an array value when 'replica': '#EQUAL` is used";
} else {
if(isNodesetObjectList){
return "cannot use array value for nodeset if replica : '#EQUAL' is not used";
}
}
}
if (condition.computedType == ComputedType.EQUAL) {
if (condition.getClause().tag != null &&
(condition.getClause().tag.op == Operand.WILDCARD || condition.getClause().tag.op == Operand.IN)) {
return null;
} else {
return "'replica': '#EQUAL` must be used with 'node':'#ANY'";
}
} else if (condition.computedType == ComputedType.ALL) {
if(isNodesetObjectList) return "replica: '#ALL' cannot be used with a list of values in nodeset";
if (condition.getClause().tag != null && (condition.getClause().getTag().op == Operand.IN ||
condition.getClause().getTag().op == Operand.WILDCARD)) {
return StrUtils.formatString("array value or wild card cannot be used for tag {0} with replica : '#ALL'",
condition.getClause().tag.getName());
}
} else {
return checkNonEqualOp(condition);
}
return null;
}
static String checkNonEqualOp(Condition condition) {
if (condition.computedType == null &&
condition.val instanceof RangeVal &&
condition.op != Operand.RANGE_EQUAL) {
return "non-integer values cannot have any other operators";
}
if(condition.computedType == ComputedType.PERCENT && condition.op != Operand.RANGE_EQUAL){
return "percentage values cannot have any other operators";
}
return null;
}
@Override
public Object computeValue(Policy.Session session, Condition cv, String collection, String shard, String node) {
if (cv.computedType == ComputedType.ALL)
return Double.valueOf(getRelevantReplicasCount(session, cv, collection, shard));
if (cv.computedType == ComputedType.EQUAL) {
int relevantReplicasCount = getRelevantReplicasCount(session, cv, collection, shard);
double bucketsCount = getNumBuckets(session, cv.getClause());
if (relevantReplicasCount == 0 || bucketsCount == 0) return 0;
return (double) relevantReplicasCount / bucketsCount;
} else if (cv.computedType == ComputedType.PERCENT) {
return ComputedType.PERCENT.compute(getRelevantReplicasCount(session, cv, collection, shard), cv);
} else {
throw new IllegalArgumentException("Unsupported type " + cv.computedType);
}
}
@SuppressWarnings({"unchecked", "rawtypes"})
private int getNumBuckets(Policy.Session session, Clause clause) {
if (clause.getTag().getOperand() == Operand.IN) {
return ((Collection) clause.getTag().val).size();
} else if (clause.getTag().getOperand() == Operand.WILDCARD) {
if (clause.getTag().varType == Type.NODE) return session.matrix.size();
Set uniqueVals = new HashSet();
for (Row matrix : session.matrix) {
Object val = matrix.getVal(clause.getTag().name);
if (val != null) uniqueVals.add(val);
}
return uniqueVals.size();
} else {
throw new IllegalArgumentException("Invalid operand for the tag in " + clause);
}
}
}