mirror of
https://github.com/nosqlbench/nosqlbench.git
synced 2025-02-25 18:55:28 -06:00
enable label filtering and validation
This commit is contained in:
parent
31a75cd2e9
commit
186e87ac1b
@ -16,6 +16,7 @@
|
||||
|
||||
package io.nosqlbench.engine.api.activityapi.cyclelog.filters.tristate;
|
||||
|
||||
import io.nosqlbench.api.filtering.TristateFilter;
|
||||
import io.nosqlbench.engine.api.activityapi.cyclelog.buffers.results.ResultReadable;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
package io.nosqlbench.engine.api.activityapi.cyclelog.filters.tristate;
|
||||
|
||||
import io.nosqlbench.api.filtering.TristateFilter;
|
||||
import io.nosqlbench.engine.api.activityapi.cyclelog.buffers.results.ResultReadable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
package io.nosqlbench.engine.api.activityapi.cyclelog.filters.tristate;
|
||||
|
||||
import io.nosqlbench.api.filtering.TristateFilter;
|
||||
import io.nosqlbench.engine.api.activityapi.cyclelog.buffers.results.ResultReadable;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
@ -19,7 +19,7 @@ package io.nosqlbench.engine.api.activityapi.cyclelog.tristate;
|
||||
import io.nosqlbench.engine.api.activityapi.cyclelog.buffers.results.MutableCycleResult;
|
||||
import io.nosqlbench.engine.api.activityapi.cyclelog.buffers.results.ResultReadable;
|
||||
import io.nosqlbench.engine.api.activityapi.cyclelog.filters.tristate.ResultFilteringSieve;
|
||||
import io.nosqlbench.engine.api.activityapi.cyclelog.filters.tristate.TristateFilter;
|
||||
import io.nosqlbench.api.filtering.TristateFilter;
|
||||
import io.nosqlbench.engine.api.activityapi.cyclelog.buffers.results.CycleResult;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
|
@ -400,7 +400,7 @@ public class NBCLI implements Function<String[], Integer>, NBLabeledElement {
|
||||
}
|
||||
|
||||
NBCLI.logger.debug("initializing annotators with config:'{}'", annotatorsConfig);
|
||||
Annotators.init(annotatorsConfig);
|
||||
Annotators.init(annotatorsConfig, options.getAnnotateLabelSpec());
|
||||
Annotators.recordAnnotation(
|
||||
Annotation.newBuilder()
|
||||
.element(this)
|
||||
|
@ -52,6 +52,8 @@ public class NBCLIOptions {
|
||||
private static final Map<String, String> DEFAULT_LABELS = Map.of("appname", "nosqlbench");
|
||||
private static final String METRICS_PREFIX = "--metrics-prefix";
|
||||
private static final String ANNOTATE_EVENTS = "--annotate";
|
||||
|
||||
private static final String ANNOTATE_LABELSPEC = "--annotate-labelspec";
|
||||
private static final String ANNOTATORS_CONFIG = "--annotators";
|
||||
private static final String PROMPUSH_CONFIG = "--prompush";
|
||||
|
||||
@ -200,6 +202,7 @@ public class NBCLIOptions {
|
||||
private boolean wantsListApps;
|
||||
private boolean dedicatedVerificationLogger;
|
||||
private boolean wantsConsoleMetrics =true;
|
||||
private String annotateLabelSpec="";
|
||||
|
||||
public boolean wantsLoggedMetrics() { return this.wantsConsoleMetrics; }
|
||||
public boolean isWantsListApps() {
|
||||
@ -258,6 +261,10 @@ public class NBCLIOptions {
|
||||
this.dedicatedVerificationLogger = true;
|
||||
}
|
||||
|
||||
public String getAnnotateLabelSpec() {
|
||||
return annotateLabelSpec;
|
||||
}
|
||||
|
||||
public enum Mode {
|
||||
ParseGlobalsOnly,
|
||||
ParseAllOptions
|
||||
@ -497,6 +504,9 @@ public class NBCLIOptions {
|
||||
arglist.removeFirst();
|
||||
this.wantsConsoleMetrics =false;
|
||||
break;
|
||||
case ANNOTATE_LABELSPEC:
|
||||
arglist.removeFirst();
|
||||
this.annotateLabelSpec = this.readWordOrThrow(arglist, "labels validator specification");
|
||||
default:
|
||||
nonincludes.addLast(arglist.removeFirst());
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2022 nosqlbench
|
||||
* Copyright (c) 2022-2023 nosqlbench
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -18,13 +18,10 @@ package io.nosqlbench.engine.core.annotation;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import io.nosqlbench.api.config.standard.*;
|
||||
import io.nosqlbench.nb.annotations.Service;
|
||||
import io.nosqlbench.api.annotations.Annotation;
|
||||
import io.nosqlbench.api.annotations.Annotator;
|
||||
import io.nosqlbench.api.config.standard.ConfigLoader;
|
||||
import io.nosqlbench.api.config.standard.NBConfigurable;
|
||||
import io.nosqlbench.api.config.standard.NBConfiguration;
|
||||
import io.nosqlbench.api.config.standard.NBMapConfigurable;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
@ -43,6 +40,8 @@ public class Annotators {
|
||||
private static final Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
||||
|
||||
private static List<Annotator> annotators;
|
||||
private static NBLabelsFilter filter;
|
||||
private static NBLabelsValidator validator;
|
||||
|
||||
/**
|
||||
* Initialize the active annotators. This method must be called before any others.
|
||||
@ -50,7 +49,10 @@ public class Annotators {
|
||||
* @param annotatorsConfig A (possibly empty) set of annotator configurations, in any form
|
||||
* supported by {@link ConfigLoader}
|
||||
*/
|
||||
public synchronized static void init(String annotatorsConfig) {
|
||||
public synchronized static void init(String annotatorsConfig, String annotateLabelSpec) {
|
||||
|
||||
filter = new NBLabelsFilter(annotateLabelSpec);
|
||||
validator = new NBLabelsValidator(annotateLabelSpec);
|
||||
|
||||
ConfigLoader loader = new ConfigLoader();
|
||||
annotators = new ArrayList<>();
|
||||
@ -120,6 +122,8 @@ public class Annotators {
|
||||
}
|
||||
|
||||
public static synchronized void recordAnnotation(Annotation annotation) {
|
||||
annotation.applyLabelFunction(filter);
|
||||
annotation.applyLabelFunction(validator);
|
||||
for (Annotator annotator : getAnnotators()) {
|
||||
try {
|
||||
logger.trace(() -> "calling annotator " + annotator.getClass().getAnnotation(Service.class).selector());
|
||||
|
@ -20,6 +20,7 @@ import io.nosqlbench.api.config.NBLabeledElement;
|
||||
import io.nosqlbench.api.config.NBLabels;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* This is a general purpose representation of an event that describes
|
||||
@ -75,6 +76,7 @@ public interface Annotation extends NBLabeledElement {
|
||||
*/
|
||||
NBLabels getLabels();
|
||||
|
||||
void applyLabelFunction(Function<NBLabels,NBLabels> labelfunc);
|
||||
/**
|
||||
* The details are an ordered map of all the content that you would want the user to see.
|
||||
*
|
||||
|
@ -27,8 +27,10 @@ import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Map;
|
||||
import java.util.TimeZone;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class MutableAnnotation implements Annotation {
|
||||
|
||||
@ -36,6 +38,8 @@ public class MutableAnnotation implements Annotation {
|
||||
private String session = "SESSION_UNNAMED";
|
||||
private final ZoneId GMT = ZoneId.of("GMT");
|
||||
|
||||
private final LinkedList<Function<NBLabels,NBLabels>> labelfuncs = new LinkedList<>();
|
||||
|
||||
@Expose
|
||||
private Layer layer;
|
||||
|
||||
@ -48,6 +52,8 @@ public class MutableAnnotation implements Annotation {
|
||||
@Expose
|
||||
private Map<String, String> details = new LinkedHashMap<>();
|
||||
|
||||
NBLabels labels = NBLabels.forKV();
|
||||
|
||||
private final ZoneId zoneid = ZoneId.of("GMT");
|
||||
private NBLabeledElement element;
|
||||
|
||||
@ -69,6 +75,7 @@ public class MutableAnnotation implements Annotation {
|
||||
|
||||
private void setElement(NBLabeledElement element) {
|
||||
this.element = element;
|
||||
this.labels = element.getLabels();
|
||||
}
|
||||
|
||||
public void setSession(String sessionName) {
|
||||
@ -108,7 +115,16 @@ public class MutableAnnotation implements Annotation {
|
||||
|
||||
@Override
|
||||
public NBLabels getLabels() {
|
||||
return element.getLabels();
|
||||
NBLabels labels= element.getLabels();
|
||||
for (Function<NBLabels, NBLabels> f : labelfuncs) {
|
||||
labels = f.apply(labels);
|
||||
}
|
||||
return labels;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyLabelFunction(Function<NBLabels, NBLabels> labelfunc) {
|
||||
labelfuncs.add(labelfunc);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -153,6 +153,9 @@ public class MapLabels implements NBLabels {
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
if (labels.size()==0) {
|
||||
return "{}";
|
||||
}
|
||||
StringBuilder sb = new StringBuilder("{");
|
||||
labels.forEach((k,v) -> {
|
||||
sb.append(k).append(":\\\"").append(v).append("\\\"").append(",");
|
||||
@ -204,4 +207,19 @@ public class MapLabels implements NBLabels {
|
||||
for (int i = 0; i < labelsAndValues.length; i+=2) childLabels.put(labelsAndValues[i].toString(), labelsAndValues[i + 1].toString());
|
||||
return childLabels;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
MapLabels mapLabels = (MapLabels) o;
|
||||
|
||||
return Objects.equals(labels, mapLabels.labels);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return labels != null ? labels.hashCode() : 0;
|
||||
}
|
||||
}
|
||||
|
@ -33,14 +33,14 @@ public class NBLabelSpec {
|
||||
throw new BasicError("Unable to parse labels to set:" + labeldata);
|
||||
}
|
||||
if (parts[1].startsWith("#")) {
|
||||
buf = buf.and(NBLabels.forKV().andInstances(parts[0], parts[1].substring(1)));
|
||||
buf = buf.and(NBLabels.forKV().and(parts[0], parts[1].substring(1)));
|
||||
} else if (parts[1].startsWith("$")) {
|
||||
buf = buf.and(NBLabels.forKV().andTypes(parts[0], parts[1].substring(1)));
|
||||
buf = buf.and(NBLabels.forKV().and(parts[0], parts[1].substring(1)));
|
||||
} else {
|
||||
// if (parts[1].matches(".*[0-9]+.*")) {
|
||||
// throw new BasicError("You have specified an auxiliary tag which contains numbers in its value (" + component + "), but you have not designated it as a dimension, as in " + parts[0] + ":$" + parts[1] + " or an instance value, as in " + parts[0] + ":#" + parts[1]);
|
||||
// }
|
||||
buf = buf.and(NBLabels.forKV().andTypes(parts[0], parts[1]));
|
||||
buf = buf.and(NBLabels.forKV().and(parts[0], parts[1]));
|
||||
}
|
||||
}
|
||||
return buf;
|
||||
|
@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2023 nosqlbench
|
||||
*
|
||||
* Licensed 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 io.nosqlbench.api.config.standard;
|
||||
|
||||
import io.nosqlbench.api.config.NBLabels;
|
||||
import io.nosqlbench.api.filtering.FilteringSieve;
|
||||
import io.nosqlbench.api.filtering.TristateFilter;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class NBLabelsFilter implements Function<NBLabels, NBLabels> {
|
||||
|
||||
private final FilteringSieve<String> filter;
|
||||
|
||||
public NBLabelsFilter(String config) {
|
||||
FilteringSieve.Builder<String> filterBuilder = new FilteringSieve.Builder<>();
|
||||
for (String component : config.split("[ ;,]")) {
|
||||
TristateFilter.Policy policyForStage = TristateFilter.Policy.Keep;
|
||||
if (component.startsWith("-")) {
|
||||
component = component.substring(1);
|
||||
policyForStage = TristateFilter.Policy.Discard;
|
||||
} else if (component.startsWith("+")) {
|
||||
component = component.substring(1);
|
||||
}
|
||||
Pattern pattern = Pattern.compile(component);
|
||||
filterBuilder = filterBuilder.withPhase(s -> pattern.matcher(s).matches(), policyForStage);
|
||||
}
|
||||
filterBuilder=filterBuilder.keepByDefault();
|
||||
this.filter = filterBuilder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBLabels apply(NBLabels labels) {
|
||||
Map<String, String> filteredMap = filter.filterMapKeys(labels.asMap());
|
||||
return NBLabels.forMap(filteredMap);
|
||||
}
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (c) 2023 nosqlbench
|
||||
*
|
||||
* Licensed 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 io.nosqlbench.api.config.standard;
|
||||
|
||||
import io.nosqlbench.api.config.NBLabels;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class NBLabelsValidator implements Function<NBLabels, NBLabels> {
|
||||
private final static Logger logger = LogManager.getLogger(NBLabelsValidator.class);
|
||||
|
||||
private final String config;
|
||||
private final List<String> requiredFields = new ArrayList<>();
|
||||
private final List<String> disallowedFields = new ArrayList<>();
|
||||
|
||||
public NBLabelsValidator(String config) {
|
||||
this.config = config;
|
||||
for (String component : config.split("[ ;,]")) {
|
||||
if (component.startsWith("-")) disallowedFields.add(component.substring(1));
|
||||
if (component.startsWith("+")) requiredFields.add(component.substring(1));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public NBLabels apply(NBLabels labels) {
|
||||
Set<String> keyset = labels.asMap().keySet();
|
||||
LinkedList<String> missingFields = new LinkedList<>(requiredFields);
|
||||
LinkedList<String> extraneousFields = new LinkedList<>(disallowedFields);
|
||||
missingFields.removeIf(keyset::contains);
|
||||
extraneousFields.removeIf(extra -> !keyset.contains(extra));
|
||||
|
||||
Result result = new Result(config, missingFields, extraneousFields);
|
||||
if (!result.isError()) {
|
||||
return labels;
|
||||
}
|
||||
logger.warn(result);
|
||||
throw new RuntimeException(result.toString());
|
||||
}
|
||||
|
||||
|
||||
record Result(String config, LinkedList<String> missingFields, LinkedList<String> extraneousFields) {
|
||||
public boolean isError() {
|
||||
return !missingFields.isEmpty() || !extraneousFields.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder err = new StringBuilder();
|
||||
if (!missingFields.isEmpty()) {
|
||||
err.append("The label set is missing required label names: ").append(missingFields).append("\n");
|
||||
}
|
||||
if (!extraneousFields.isEmpty()) {
|
||||
err.append("The label set has disallowed label names: ").append(extraneousFields).append("\n");
|
||||
}
|
||||
if (!err.isEmpty()) {
|
||||
err.append("This is controlled by the labeling policy: '").append(config).append("'\n");
|
||||
}
|
||||
return err.toString();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2023 nosqlbench
|
||||
*
|
||||
* Licensed 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 io.nosqlbench.api.filtering;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class FilterPhase<T> implements TristateFilter<T> {
|
||||
private final Predicate<T> predicate;
|
||||
private final Policy policy;
|
||||
|
||||
public FilterPhase(Predicate<T> predicate, Policy policy) {
|
||||
this.predicate = predicate;
|
||||
this.policy = policy;
|
||||
}
|
||||
@Override
|
||||
public Policy apply(T cycleResult) {
|
||||
if (predicate.test(cycleResult)) {
|
||||
return policy;
|
||||
}
|
||||
return Policy.Ignore;
|
||||
}
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (c) 2023 nosqlbench
|
||||
*
|
||||
* Licensed 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 io.nosqlbench.api.filtering;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class FilteringSieve<T> implements TristateFilter<T>{
|
||||
|
||||
private final List<TristateFilter<T>> sieve;
|
||||
private final Policy defaultPolicy;
|
||||
|
||||
public FilteringSieve(Policy defaultPolicy, List<TristateFilter<T>> filterPhases) {
|
||||
this.sieve = filterPhases;
|
||||
this.defaultPolicy = defaultPolicy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Policy apply(T element) {
|
||||
for (TristateFilter<T> tTristateFilter : sieve) {
|
||||
Policy phaseResult = tTristateFilter.apply(element);
|
||||
if (phaseResult!=Policy.Ignore) return phaseResult;
|
||||
}
|
||||
return defaultPolicy;
|
||||
}
|
||||
|
||||
public static class Builder<T> {
|
||||
private final List<TristateFilter<T>> phaseFilters = new ArrayList<>();
|
||||
private Policy defaultPolicy = Policy.Ignore;
|
||||
|
||||
public Builder<T> keepByDefault() {
|
||||
this.defaultPolicy = Policy.Keep;
|
||||
return this;
|
||||
}
|
||||
public Builder<T> discardByDefault() {
|
||||
this.defaultPolicy = Policy.Discard;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<T> withPhase(Predicate<T> predicate, Policy policy) {
|
||||
this.phaseFilters.add(new FilterPhase<>(predicate,policy));
|
||||
return this;
|
||||
}
|
||||
public Builder<T> withPhase(TristateFilter<T> phaseFilter) {
|
||||
this.phaseFilters.add(phaseFilter);
|
||||
return this;
|
||||
}
|
||||
|
||||
public FilteringSieve<T> build() {
|
||||
return new FilteringSieve<>(defaultPolicy, phaseFilters);
|
||||
}
|
||||
}
|
||||
|
||||
public List<T> filterList(ArrayList<T> input) {
|
||||
ArrayList<T> result = new ArrayList<T>();
|
||||
for (T in : input) if (apply(in).equals(TristateFilter.Policy.Keep)) result.add(in);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public <U> Map<T,U> filterMapKeys(Map<T,U> input) {
|
||||
Map<T,U> result = new LinkedHashMap<T,U>();
|
||||
for (T k : input.keySet()) {
|
||||
if (apply(k).equals(Policy.Keep)) {
|
||||
result.put(k,input.get(k));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
@ -14,23 +14,24 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package io.nosqlbench.engine.api.activityapi.cyclelog.filters.tristate;
|
||||
package io.nosqlbench.api.filtering;
|
||||
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* A tri-state filter allows for flexible configuration of
|
||||
* <P>A tri-state filter allows for flexible configuration of
|
||||
* multi-phase filtering. It effectively allows a conditional behavior
|
||||
* for filtering logic that can answer "yes", "no", <em>and</em> "I don't know."
|
||||
* </P>
|
||||
*
|
||||
* This can also be used to build classic bi-state filtering, such as
|
||||
* <P>This can also be used to build classic bi-state filtering, such as
|
||||
* filters that use a boolean predicate to say "keep" or "discard." Where the
|
||||
* tri-state filtering pattern shines, however, is in the ability to combine
|
||||
* different filtering rules to build a sophisticated filter at run-time
|
||||
* that bi-state filtering would prevent.
|
||||
* that bi-state filtering would prevent.</P>
|
||||
*
|
||||
* In contrast to the bi-state filter, the default policy that is applied when
|
||||
* <P>In contrast to the bi-state filter, the default policy that is applied when
|
||||
* <em>not</em> matching an item with the predicate is to simply ignore it.
|
||||
* This means that in order to implement both matching and discarding
|
||||
* policies like a bi-state filter, you must do one of the following:
|
||||
@ -38,22 +39,22 @@ import java.util.function.Predicate;
|
||||
* <li>Implement a default policy that overrides the "Ignore" action.</li>
|
||||
* <li>Use both "keep" and "discard" predicates together in sequence.</li>
|
||||
* </ul>
|
||||
* </P>
|
||||
*
|
||||
* The two techniques above are not mutually exclusive. In practice, tri-state
|
||||
* <P>The two techniques above are not mutually exclusive. In practice, tri-state
|
||||
* filters are used to build up chains of filters which can delegate down
|
||||
* the chain if up-stream filters do not have enough information to make
|
||||
* a keep or discard determination. Even in chained filters will have a
|
||||
* default policy that will override the "ignore" outcome.
|
||||
* default policy that will override the "ignore" outcome.</P>
|
||||
*
|
||||
* Filter chains that
|
||||
* <P>Filter chains that
|
||||
* have a default "exclude" policy that overrides "ignore" policies are
|
||||
* called "exclusive" filters. Their counterparts are "inclusive" filters.
|
||||
* In other words, Exclusive tri-state filters include an element if and only
|
||||
* if there was a matching include rule before any matching exclude rules
|
||||
* or the end of the filter chain.
|
||||
* Inclusive tri-state filters exclude an element if and only if there was
|
||||
* a matching exclude rule before any matching include rules or the end of
|
||||
* the filter chain.
|
||||
* or the end of the filter chain, whereas inclusive tri-state filters exclude
|
||||
* an element if and only if there was a matching exclude rule before any matching
|
||||
* include rules or the end of the filter chain.</P>
|
||||
*/
|
||||
public interface TristateFilter<T> extends Function<T, TristateFilter.Policy> {
|
||||
|
||||
@ -62,9 +63,8 @@ public interface TristateFilter<T> extends Function<T, TristateFilter.Policy> {
|
||||
|
||||
/**
|
||||
* The filter action determines what action is taken for a given
|
||||
* element that matches the predicate. If the whether to include or exclude a result
|
||||
* of the filter matching. If the filter does not match, then neither
|
||||
* include nor exclude are presumed. See the class docs for more details.
|
||||
* element that matches the predicate.
|
||||
*/
|
||||
enum Policy {
|
||||
Keep,
|
||||
@ -74,11 +74,17 @@ public interface TristateFilter<T> extends Function<T, TristateFilter.Policy> {
|
||||
|
||||
/**
|
||||
* Create a predicate that will override any Ignore outcomes with the provided policy.
|
||||
* @param defaultPolicy The policy that will override non-actionable outcomes
|
||||
*
|
||||
* @param defaultPolicy
|
||||
* The policy that will override non-actionable outcomes
|
||||
* @return a Predicate that can be used to filter elements
|
||||
*/
|
||||
default Predicate<T> toDefaultingPredicate(Policy defaultPolicy) {
|
||||
return new DefaultingPredicate<>(this,defaultPolicy);
|
||||
return new DefaultingPredicate<>(this, defaultPolicy);
|
||||
}
|
||||
|
||||
default TristateFilter<T> toDefaultingFilter(Policy defaultPolicy) {
|
||||
return new DefaultingTriStateFilter(this,defaultPolicy);
|
||||
}
|
||||
|
||||
class DefaultingPredicate<T> implements Predicate<T> {
|
||||
@ -93,10 +99,10 @@ public interface TristateFilter<T> extends Function<T, TristateFilter.Policy> {
|
||||
@Override
|
||||
public boolean test(T t) {
|
||||
Policy policyResult = filter.apply(t);
|
||||
if (policyResult==Policy.Ignore) {
|
||||
policyResult= defaultPolicy;
|
||||
if (policyResult == Policy.Ignore) {
|
||||
policyResult = defaultPolicy;
|
||||
}
|
||||
return policyResult==Policy.Keep;
|
||||
return policyResult == Policy.Keep;
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,11 +110,13 @@ public interface TristateFilter<T> extends Function<T, TristateFilter.Policy> {
|
||||
/**
|
||||
* Create a predicate that will return true if and only if the filter
|
||||
* outcome matches the provided policy.
|
||||
* @param matchingPolicy The policy that will signal true in the predicate.
|
||||
*
|
||||
* @param matchingPolicy
|
||||
* The policy that will signal true in the predicate.
|
||||
* @return a Predicate that can be used to filter elements
|
||||
*/
|
||||
default Predicate<T> toMatchingPredicate(Policy matchingPolicy) {
|
||||
return new MatchingPredicate<>(this,matchingPolicy);
|
||||
return new MatchingPredicate<>(this, matchingPolicy);
|
||||
}
|
||||
|
||||
class MatchingPredicate<T> implements Predicate<T> {
|
||||
@ -122,9 +130,25 @@ public interface TristateFilter<T> extends Function<T, TristateFilter.Policy> {
|
||||
|
||||
@Override
|
||||
public boolean test(T t) {
|
||||
return filter.apply(t)==matchOn;
|
||||
return filter.apply(t) == matchOn;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class DefaultingTriStateFilter<U> implements TristateFilter<U> {
|
||||
private final TristateFilter<U> wrappedFilter;
|
||||
private final Policy defaultPolicy;
|
||||
|
||||
public DefaultingTriStateFilter(TristateFilter<U> innerFilter, Policy defaultPolicy) {
|
||||
this.wrappedFilter = innerFilter;
|
||||
this.defaultPolicy = defaultPolicy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Policy apply(U cycleResult) {
|
||||
Policy policy = wrappedFilter.apply(cycleResult);
|
||||
if (policy!=Policy.Ignore) return policy;
|
||||
return defaultPolicy;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2023 nosqlbench
|
||||
*
|
||||
* Licensed 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 io.nosqlbench.api.config.standard;
|
||||
|
||||
import io.nosqlbench.api.config.NBLabels;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class NBLabelsFilterTest {
|
||||
|
||||
@Test
|
||||
public void testFilterLablesWildcardExclude() {
|
||||
NBLabelsFilter f1 = new NBLabelsFilter("+abcd,-.*");
|
||||
assertThat(f1.apply(NBLabels.forKV("abc", "def"))).isEqualTo(NBLabels.forKV());
|
||||
assertThat(f1.apply(NBLabels.forKV("abcd", "defg"))).isEqualTo(NBLabels.forKV("abcd", "defg"));
|
||||
assertThat(f1.apply(NBLabels.forKV("a_bcd", "def"))).isEqualTo(NBLabels.forKV());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFilterLabelsWildcardInclude(){
|
||||
NBLabelsFilter f2 = new NBLabelsFilter("-abcd,+.*");
|
||||
assertThat(f2.apply(NBLabels.forKV("abc", "def"))).isEqualTo(NBLabels.forKV("abc", "def"));
|
||||
assertThat(f2.apply(NBLabels.forKV("abcd", "defg","hijk","lmnop"))).isEqualTo(NBLabels.forKV("hijk","lmnop"));
|
||||
assertThat(f2.apply(NBLabels.forKV("a_bcd", "def"))).isEqualTo(NBLabels.forKV("a_bcd","def"));}
|
||||
|
||||
@Test
|
||||
public void testFilteredLabelsBasicNames() {
|
||||
NBLabelsFilter f3 = new NBLabelsFilter("abcd,bcde,-fghi");
|
||||
assertThat(f3.apply(NBLabels.forKV("abc", "def", "abcd","abcd"))).isEqualTo(NBLabels.forKV("abc","def","abcd", "abcd"));
|
||||
assertThat(f3.apply(NBLabels.forKV("abcd", "defg"))).isEqualTo(NBLabels.forKV("abcd","defg"));
|
||||
assertThat(f3.apply(NBLabels.forKV("bcde","sdf","a_bcd", "def","fghi","fghi"))).isEqualTo(NBLabels.forKV("bcde","sdf","a_bcd","def"));
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2023 nosqlbench
|
||||
*
|
||||
* Licensed 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 io.nosqlbench.api.config.standard;
|
||||
|
||||
import io.nosqlbench.api.config.NBLabels;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class NBLabelsValidatorTest {
|
||||
|
||||
@Test
|
||||
public void testNoViolations() {
|
||||
NBLabelsValidator lv = new NBLabelsValidator("+needed -disallowed indifferent");
|
||||
assertThat(lv.apply(NBLabels.forKV("needed","haveit","indifferent","isalsoallowed")))
|
||||
.isEqualTo(NBLabels.forKV("needed","haveit","indifferent","isalsoallowed"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExtraneousViolations() {
|
||||
NBLabelsValidator lv = new NBLabelsValidator("+needed -disallowed indifferent");
|
||||
assertThrows(RuntimeException.class, () -> lv.apply(NBLabels.forKV("needed","haveit","indifferent","isalsoallowed","disallowed","neversaynever")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void TestMissingViolations() {
|
||||
NBLabelsValidator lv = new NBLabelsValidator("+needed -disallowed indifferent");
|
||||
assertThrows(RuntimeException.class, () -> lv.apply(NBLabels.forKV("indifferent","isalsoallowed","unrelated","events")));
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2023 nosqlbench
|
||||
*
|
||||
* Licensed 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 io.nosqlbench.api.filtering;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class FilteringSieveTest {
|
||||
@Test
|
||||
public void testStringFilteringSieve() {
|
||||
FilteringSieve<String> filter = new FilteringSieve.Builder<String>()
|
||||
.withPhase(s -> s.equals("amatch"), TristateFilter.Policy.Keep)
|
||||
.withPhase(s -> s.equals("a mismatch"), TristateFilter.Policy.Discard)
|
||||
.build();
|
||||
|
||||
assertThat(filter.apply("amatch")).isEqualTo(TristateFilter.Policy.Keep);
|
||||
assertThat(filter.apply("a mismatch")).isEqualTo(TristateFilter.Policy.Discard);
|
||||
assertThat(filter.apply("a something else")).isEqualTo(TristateFilter.Policy.Ignore);
|
||||
|
||||
TristateFilter<String> butDiscard = filter.toDefaultingFilter(TristateFilter.Policy.Discard);
|
||||
assertThat(butDiscard.apply("a something else")).isEqualTo(TristateFilter.Policy.Discard);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user