blob: e6bd5e73a6087770747b29ee19e9d7deca209913 [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.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import org.apache.solr.common.cloud.rule.ImplicitSnitch;
import static java.util.Collections.emptySet;
import static java.util.Collections.unmodifiableMap;
import static java.util.Collections.unmodifiableSet;
/**
* A Variable Type used in Autoscaling policy rules. Each variable type may have unique implementation
* of functionalities
*
* @deprecated to be removed in Solr 9.0 (see SOLR-14656)
*/
public interface Variable {
String NULL = "";
String coreidxsize = "INDEX.sizeInGB";
default boolean match(Object inputVal, Operand op, Object val, String name, Row row) {
return op.match(val, validate(name, inputVal, false)) == Clause.TestStatus.PASS;
}
default Object convertVal(Object val) {
return val;
}
default void projectAddReplica(Cell cell, ReplicaInfo ri, Consumer<Row.OperationInfo> opCollector, boolean strictMode) {
}
default boolean addViolatingReplicas(Violation.Ctx ctx) {
return false;
}
void getSuggestions(Suggestion.Ctx ctx);
/**When a non constant value is used in a variable, the actual value needs to be computed at the runtime
*
*/
default Object computeValue(Policy.Session session, Condition condition, String collection, String shard, String node) {
return condition.val;
}
default void computeDeviation(Policy.Session session, double[] deviations, ReplicaCount replicaCount, SealedClause sealedClause) {
if (deviations != null) {
Number actualCount = replicaCount.getVal(sealedClause.type);
if(sealedClause.replica.val instanceof RangeVal) {
Double realDelta = ((RangeVal) sealedClause.replica.val).realDelta(actualCount.doubleValue());
realDelta = sealedClause.isReplicaZero() ? -1 * realDelta : realDelta;
deviations[0] += Math.abs(realDelta);
}
}
}
int compareViolation(Violation v1, Violation v2);
default void projectRemoveReplica(Cell cell, ReplicaInfo ri, Consumer<Row.OperationInfo> opCollector) {
}
default String postValidate(Condition condition) {
return null;
}
default Operand getOperand(Operand expected, Object strVal, ComputedType computedType) {
return expected;
}
Object validate(String name, Object val, boolean isRuleVal);
/**
* Type details of each variable in policies
*/
public enum Type implements Variable {
@Meta(name = "withCollection",
type = String.class,
isNodeSpecificVal = true,
implementation = WithCollectionVariable.class)
WITH_COLLECTION(),
@Meta(name = "collection",
type = String.class)
COLL,
@Meta(
name = "shard",
type = String.class,
wildCards = {Policy.EACH, Policy.ANY})
SHARD(),
@Meta(name = "replica",
type = Double.class,
min = 0, max = -1,
implementation = ReplicaVariable.class,
computedValues = {ComputedType.EQUAL, ComputedType.PERCENT, ComputedType.ALL})
REPLICA,
@Meta(name = ImplicitSnitch.PORT,
type = Long.class,
min = 1,
max = 65535,
supportArrayVals = true,
wildCards = Policy.EACH
)
PORT,
@Meta(name = "ip_1",
type = Long.class,
min = 0,
max = 255,
supportArrayVals = true,
wildCards = Policy.EACH)
IP_1,
@Meta(name = "ip_2",
type = Long.class,
min = 0,
max = 255,
supportArrayVals = true,
wildCards = Policy.EACH)
IP_2,
@Meta(name = "ip_3",
type = Long.class,
min = 0,
max = 255,
supportArrayVals = true,
wildCards = Policy.EACH)
IP_3,
@Meta(name = "ip_4",
type = Long.class,
min = 0,
max = 255,
supportArrayVals = true,
wildCards = Policy.EACH)
IP_4,
@Meta(name = ImplicitSnitch.DISK,
type = Double.class,
min = 0,
isNodeSpecificVal = true,
associatedPerReplicaValue = Variable.coreidxsize,
associatedPerNodeValue = "totaldisk",
implementation = FreeDiskVariable.class,
computedValues = ComputedType.PERCENT)
FREEDISK,
@Meta(name = "totaldisk",
type = Double.class,
isHidden = true, implementation = VariableBase.TotalDiskVariable.class)
TOTALDISK,
@Meta(name = Variable.coreidxsize,
type = Double.class,
isNodeSpecificVal = true,
isHidden = true,
min = 0,
implementation = VariableBase.CoreIndexSizeVariable.class,
metricsKey = "INDEX.sizeInBytes")
CORE_IDX,
@Meta(name = ImplicitSnitch.NODEROLE,
type = String.class,
enumVals = "overseer")
NODE_ROLE,
@Meta(name = ImplicitSnitch.CORES,
type = Double.class,
min = 0, max = -1,
computedValues = {ComputedType.EQUAL, ComputedType.PERCENT},
implementation = CoresVariable.class)
CORES,
@Meta(name = ImplicitSnitch.SYSLOADAVG,
type = Double.class,
min = 0,
max = 100,
isNodeSpecificVal = true)
SYSLOADAVG,
@Meta(name = ImplicitSnitch.HEAPUSAGE,
type = Double.class,
min = 0,
isNodeSpecificVal = true)
HEAPUSAGE,
@Meta(name = "NUMBER",
type = Long.class,
min = 0)
NUMBER,
@Meta(name = "host",
type = String.class,
wildCards = Policy.EACH,
supportArrayVals = true)
HOST,
@Meta(name = "STRING",
type = String.class,
wildCards = Policy.EACH,
supportArrayVals = true
)
SYSPROP,
@Meta(name = "node",
type = String.class,
isNodeSpecificVal = true,
wildCards = {Policy.ANY, Policy.EACH},
implementation = NodeVariable.class,
supportArrayVals = true)
NODE,
@Meta(name = "LAZY",
type = void.class,
implementation = VariableBase.LazyVariable.class)
LAZY,
@Meta(name = ImplicitSnitch.DISKTYPE,
type = String.class,
enumVals = {"ssd", "rotational"},
implementation = VariableBase.DiskTypeVariable.class,
supportArrayVals = true)
DISKTYPE;
public final String tagName;
@SuppressWarnings({"rawtypes"})
public final Class type;
public Meta meta;
public final Set<String> vals;
public final Number min;
public final Number max;
public final Boolean additive;
public final Set<String> wildCards;
public final String perReplicaValue;
public final Set<String> associatedPerNodeValues;
public final String metricsAttribute;
public final Set<ComputedType> supportedComputedTypes;
final Variable impl;
Type() {
try {
meta = Type.class.getField(name()).getAnnotation(Meta.class);
if (meta == null) {
throw new RuntimeException("Invalid type, should have a @Meta annotation " + name());
}
} catch (NoSuchFieldException e) {
//cannot happen
}
impl = VariableBase.loadImpl(meta, this);
this.tagName = meta.name();
this.type = meta.type();
this.vals = readSet(meta.enumVals());
this.max = readNum(meta.max());
this.min = readNum(meta.min());
this.perReplicaValue = readStr(meta.associatedPerReplicaValue());
this.associatedPerNodeValues = readSet(meta.associatedPerNodeValue());
this.additive = meta.isAdditive();
this.metricsAttribute = readStr(meta.metricsKey());
this.supportedComputedTypes = meta.computedValues()[0] == ComputedType.NULL ?
emptySet() :
unmodifiableSet(new HashSet(Arrays.asList(meta.computedValues())));
this.wildCards = readSet(meta.wildCards());
}
public String getTagName() {
return meta.name();
}
private String readStr(String s) {
return NULL.equals(s) ? null : s;
}
private Number readNum(double v) {
return v == -1 ? null :
(Number) validate(null, v, true);
}
Set<String> readSet(String[] vals) {
if (NULL.equals(vals[0])) return emptySet();
return unmodifiableSet(new HashSet<>(Arrays.asList(vals)));
}
@Override
public void getSuggestions(Suggestion.Ctx ctx) {
impl.getSuggestions(ctx);
}
@Override
public boolean addViolatingReplicas(Violation.Ctx ctx) {
return impl.addViolatingReplicas(ctx);
}
public Operand getOperand(Operand expected, Object val, ComputedType computedType) {
return impl.getOperand(expected, val, computedType);
}
public Object convertVal(Object val) {
return impl.convertVal(val);
}
public String postValidate(Condition condition) {
return impl.postValidate(condition);
}
public Object validate(String name, Object val, boolean isRuleVal) {
return impl.validate(name, val, isRuleVal);
}
/**
* Simulate a replica addition to a node in the cluster
*/
public void projectAddReplica(Cell cell, ReplicaInfo ri, Consumer<Row.OperationInfo> opCollector, boolean strictMode) {
impl.projectAddReplica(cell, ri, opCollector, strictMode);
}
public void projectRemoveReplica(Cell cell, ReplicaInfo ri, Consumer<Row.OperationInfo> opCollector) {
impl.projectRemoveReplica(cell, ri, opCollector);
}
@Override
public int compareViolation(Violation v1, Violation v2) {
return impl.compareViolation(v1, v2);
}
@Override
public Object computeValue(Policy.Session session, Condition condition, String collection, String shard, String node) {
return impl.computeValue(session, condition, collection, shard, node);
}
@Override
public void computeDeviation(Policy.Session session, double[] deviations, ReplicaCount replicaCount, SealedClause sealedClause) {
impl.computeDeviation(session, deviations, replicaCount, sealedClause);
}
@Override
public boolean match(Object inputVal, Operand op, Object val, String name, Row row) {
return impl.match(inputVal, op, val, name, row);
}
private static final Map<String, Type> typeByNameMap;
static {
HashMap<String, Type> m = new HashMap<>();
for (Type t : Type.values()) {
m.put(t.tagName, t);
}
typeByNameMap = unmodifiableMap(m);
}
static Type get(String name) {
return typeByNameMap.get(name);
}
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Meta {
String name();
@SuppressWarnings({"rawtypes"})
Class type();
String[] associatedPerNodeValue() default NULL;
String associatedPerReplicaValue() default NULL;
String[] enumVals() default NULL;
String[] wildCards() default NULL;
boolean isNodeSpecificVal() default false;
boolean isHidden() default false;
boolean isAdditive() default true;
double min() default -1d;
double max() default -1d;
boolean supportArrayVals() default false;
String metricsKey() default NULL;
@SuppressWarnings({"rawtypes"})
Class implementation() default void.class;
ComputedType[] computedValues() default ComputedType.NULL;
}
}