temp checkpoint

This commit is contained in:
Jonathan Shook
2024-12-17 15:33:15 -06:00
parent 7153ef6ee1
commit 8c62576ff1
23 changed files with 915 additions and 253 deletions

View File

@@ -105,7 +105,6 @@ public class OpDef extends OpTemplate {
private LinkedHashMap<String, String> composeTags() {
LinkedHashMap<String, String> tagsWithName = new LinkedHashMap<>(new MultiMapLookup<>(rawOpDef.getTags(), block.getTags()));
tagsWithName.put("block",block.getName());
tagsWithName.put("name",this.rawOpDef.getName());
tagsWithName.put("op",this.rawOpDef.getName());
return tagsWithName;
}

View File

@@ -19,6 +19,8 @@ package io.nosqlbench.adapters.api.activityimpl;
import com.codahale.metrics.Timer;
import groovy.lang.Binding;
import io.nosqlbench.adapters.api.activityimpl.uniform.Space;
import io.nosqlbench.adapters.api.activityimpl.uniform.Validator;
import io.nosqlbench.adapters.api.activityimpl.uniform.ValidatorSource;
import io.nosqlbench.adapters.api.activityimpl.uniform.flowtypes.CycleOp;
import io.nosqlbench.adapters.api.evalctx.*;
import io.nosqlbench.adapters.api.metrics.ThreadLocalNamedTimers;
@@ -35,6 +37,7 @@ import org.apache.logging.log4j.Logger;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.function.LongFunction;
@@ -48,7 +51,9 @@ import java.util.function.LongFunction;
* The type of operation
*/
public abstract class BaseOpDispenser<OP extends CycleOp<?>, SPACE extends Space>
extends NBBaseComponent implements OpDispenser<OP> {
extends NBBaseComponent
implements OpDispenser<OP>, ValidatorSource
{
protected final static Logger logger = LogManager.getLogger(BaseOpDispenser.class);
public static final String VERIFIER = "verifier";
public static final String VERIFIER_INIT = "verifier-init";
@@ -230,4 +235,9 @@ public abstract class BaseOpDispenser<OP extends CycleOp<?>, SPACE extends Space
OP op = getOp(value);
return op;
}
@Override
public List<Validator> getValidator(NBComponent parent, ParsedOp pop, OpLookup lookup) {
return CoreOpValidators.getValidator(this, pop, lookup);
}
}

View File

@@ -0,0 +1,51 @@
package io.nosqlbench.adapters.api.activityimpl;
/*
* Copyright (c) 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.
*/
import io.nosqlbench.adapters.api.activityimpl.uniform.Validator;
import io.nosqlbench.adapters.api.templating.ParsedOp;
import io.nosqlbench.engine.api.templating.TypeAndTarget;
import io.nosqlbench.nb.api.components.core.NBComponent;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class CoreOpValidators {
private static final Logger logger = LogManager.getLogger(CoreOpValidators.class);
public static List<Validator> getValidator(NBComponent parent, ParsedOp pop, OpLookup lookup) {
List<Validator> validators = new ArrayList();
Optional<TypeAndTarget<CoreValidators, Object>> optionalValidator = pop.getOptionalTypeAndTargetEnum(
CoreValidators.class, Object.class);
if (optionalValidator.isPresent()) {
TypeAndTarget<CoreValidators, Object> validator = optionalValidator.get();
logger.debug("found validator '" + validator.enumId.name() + "' for op '" + pop.getName() + "'");
switch (validator.enumId) {
case verify_fields:
validators.add(new FieldVerifier(parent, pop, lookup));
}
}
return validators;
}
}

View File

@@ -0,0 +1,30 @@
package io.nosqlbench.adapters.api.activityimpl;
/*
* Copyright (c) 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.
*/
import io.nosqlbench.adapters.api.activityimpl.uniform.Validator;
public enum CoreValidators {
verify_fields(FieldVerifier.class);
private final Class<? extends Validator> validatorImpl;
CoreValidators(Class<? extends Validator> validatorClass) {
this.validatorImpl = validatorClass;
}
}

View File

@@ -0,0 +1,237 @@
package io.nosqlbench.adapters.api.activityimpl;
/*
* Copyright (c) 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.
*/
import io.nosqlbench.adapters.api.activityimpl.uniform.Validator;
import io.nosqlbench.adapters.api.activityimpl.uniform.opwrappers.DiffType;
import io.nosqlbench.adapters.api.templating.ParsedOp;
import io.nosqlbench.nb.api.components.core.NBComponent;
import io.nosqlbench.nb.api.engine.metrics.instruments.MetricCategory;
import io.nosqlbench.nb.api.engine.metrics.instruments.NBMetricCounter;
import io.nosqlbench.nb.api.errors.OpConfigError;
import io.nosqlbench.virtdata.core.templates.BindPoint;
import io.nosqlbench.virtdata.core.templates.CapturePoints;
import java.util.*;
import java.util.function.LongFunction;
import java.util.stream.Collectors;
// TODO: Make op(verifyref) use tags, and require 1
public class FieldVerifier implements Validator {
private final LongFunction<Map<String, Object>> expectedValuesF;
private final DiffType diffType;
private final NBMetricCounter resultsVerifiedError;
private final NBMetricCounter resultsOkCounter;
private final NBMetricCounter verifiedFieldsCounter;
private final String[] fieldNames;
private final String[] bindingNames;
public FieldVerifier(NBComponent parent, ParsedOp pop, OpLookup lookup) {
this.resultsVerifiedError = parent.create().counter(
"results_verified_error", MetricCategory.Verification,
"The number of results which have been verified with no error"
);
this.resultsOkCounter = parent.create().counter(
"results_verified_ok",
MetricCategory.Verification,
"The number of results which had " + "a verification error"
);
this.verifiedFieldsCounter = parent.create().counter(
"field_verified_ok", MetricCategory.Verification,
"the number of fields in results which have been verified with no error"
);
this.diffType = pop.takeEnumFromFieldOr(DiffType.class, DiffType.all, "compare");
List<String> fields = new ArrayList<>();
List<String> bindings = new ArrayList<>();
CapturePoints captures = pop.getCaptures();
ParsedOp config = pop.takeAsSubConfig("verify_fields");
Optional<Object> vspec = config.takeOptionalStaticValue("verify_fields", Object.class);
if (vspec.isPresent()) {
Object vspeco = vspec.get();
if (vspeco instanceof Map verifyers) {
verifyers.forEach((k, v) -> {
if (k instanceof CharSequence keyName && v instanceof CharSequence keyValue) {
fields.add(keyName.toString());
bindings.add(keyValue.toString());
} else {
throw new RuntimeException(
"Strings must be used in map form of " + "verify_field");
}
});
} else if (vspeco instanceof String verifyBindingSpec) {
parseFieldSpec(verifyBindingSpec, lookup, fields, bindings, captures, pop);
} else {
throw new OpConfigError("Unrecognized type for verify_fields value:" + vspeco.getClass().getSimpleName());
}
} else {
config.getDefinedNames().forEach(name -> {
fields.add(name);
bindings.add(config.getStaticValue(name));
});
}
List<BindPoint> bindPoints = pop.getBindPoints();
// Optional<String> vb = config.getOptionalStaticValue("verify_bindings", String.class);
// if (vb.isPresent()) {
// String verifyBindingSpec = vb.get();
// if (verifyBindingSpec.startsWith("op(") && verifyBindingSpec.endsWith(")")) {
// String toLookup = verifyBindingSpec.substring(2, verifyBindingSpec.lastIndexOf(-1));
// ParsedOp referenced = lookup.lookup(toLookup).orElseThrow();
// }
// }
this.fieldNames = fields.toArray(new String[fields.size()]);
this.bindingNames = bindings.toArray(new String[bindings.size()]);
this.expectedValuesF = pop.newOrderedMapBinder(bindingNames);
}
private void parseFieldSpec(
String fieldSpec, OpLookup lookup, List<String> fields,
List<String> bindings, CapturePoints captures, ParsedOp pop
) {
if (fieldSpec.startsWith("op(") && fieldSpec.endsWith(")")) {
String toLookup = fieldSpec.substring("op(".length(), fieldSpec.length() - 1);
Optional<ParsedOp> referenced = lookup.lookup(toLookup);
if (referenced.isPresent()) {
List<String> vars = referenced.get().getBindPoints().stream().map(
bp -> bp.getAnchor()).toList();
fields.addAll(vars);
bindings.addAll(vars);
} else {
throw new OpConfigError(
"no op found for verify setting '" + fieldSpec + "' " + "for op " + "template" + " '" + pop.getName() + "'");
}
} else {
String[] vfields = fieldSpec.split("\\s*,\\s*");
for (String vfield : vfields) {
// if (vfield.equals("*")) {
// fields.addAll(captures.getAsNames());
// fields.addAll(bindPoints.stream().map(bp -> bp.getAnchor()).toList());
// } else
if (vfield.startsWith("+")) {
fields.add(vfield.substring(1));
} else if (vfield.startsWith("-")) {
fields.remove(vfield.substring(1));
} else if (vfield.matches("\\w+(\\w+->[\\w-]+)?")) {
String[] parts = vfield.split("->", 2);
fields.add(parts[0]);
bindings.add(parts[1]);
} else {
throw new RuntimeException("unknown verify_fields format: '" + vfield + "'");
}
}
}
}
/// Compare the values of the row with the values generated.
///
/// Specifically,
/// - Ensure the same number of fields.
/// - Ensure the same pair-wise field names.
/// - Ensure that each pair of same-named fields has the same data type.
/// - Ensure that the value of each pair of fields is equal according to the equals
/// operator for the respective type.
/// @return a count of differences between the row and the reference values
@Override
public void validate(long cycle, Object data) {
if (data instanceof Map<?, ?> r) {
Map<String, ?> result = (Map<String, ?>) r;
Map<String, Object> referenceMap = this.expectedValuesF.apply(cycle);
int diff = 0;
StringBuilder logbuffer = new StringBuilder(); // make this a TL
logbuffer.setLength(0);
if (diffType.is(DiffType.reffields)) {
List<String> missingRowFields = Arrays.stream(this.fieldNames).filter(
gk -> !result.containsKey(gk)).collect(Collectors.toList());
if (missingRowFields.size() > 0) {
diff += missingRowFields.size();
logbuffer.append("\nexpected fields '");
logbuffer.append(String.join("','", missingRowFields));
logbuffer.append("' not in row.");
}
}
// if (diffType.is(DiffType.rowfields)) {
// List<String> missingRefFields = result.keySet().stream().filter(
// k -> !referenceMap.containsKey(k)).collect(Collectors.toList());
// if (missingRefFields.size() > 0) {
// diff += missingRefFields.size();
//
// logbuffer.append("\nexpected fields '");
// logbuffer.append(String.join("','", missingRefFields));
// logbuffer.append("' not in reference data: " + referenceMap);
// }
// }
if (diffType.is(DiffType.values)) {
for (int fidx = 0; fidx < fieldNames.length; fidx++) {
String fname = fieldNames[fidx];
;
String rname = bindingNames[fidx];
if (referenceMap.containsKey(rname)) {
if (referenceMap.get(rname).equals(result.get(fname))) {
verifiedFieldsCounter.inc();
} else {
logbuffer.append("\nvalue differs for '").append(fname).append("' ");
logbuffer.append("expected:'").append(
referenceMap.get(fname).toString()).append("'");
logbuffer.append(" actual:'").append(result.get(rname)).append("'");
diff++;
}
}
}
}
if (diff == 0) {
resultsVerifiedError.inc();
} else {
resultsOkCounter.inc();
throw new RuntimeException("in cycle " + cycle + ", " + logbuffer.toString());
}
} else {
throw new OpConfigError("Can only validate fields of type Map");
}
}
@Override
public String getName() {
return "verify_fields";
}
}

View File

@@ -0,0 +1,27 @@
package io.nosqlbench.adapters.api.activityimpl;
/*
* Copyright (c) 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.
*/
import io.nosqlbench.adapters.api.templating.ParsedOp;
import java.util.Optional;
public interface OpLookup {
Optional<ParsedOp> lookup(String opName);
}

View File

@@ -0,0 +1,20 @@
package io.nosqlbench.adapters.api.activityimpl.uniform;
import java.util.Map;
import io.nosqlbench.adapters.api.activityimpl.OpDispenser;
import io.nosqlbench.nb.api.components.core.NBNamedElement;
import io.nosqlbench.virtdata.core.templates.BindPoint;
/// This optional type allows for [OpDispenser] (or other) implementations to
/// map native field names to their associated binding names. Often, the
/// adapter-native logic is the only place this association can be derived, although
/// it is sometimes needed in core adapter-agnostic logic.
public interface FieldBindingsMetadata<FIELDTYPE> {
/// Get the map of native fields to bind points.
/// The bind points don't need to be the same actual object which is used, but both the
/// field names and the binding points should be equivalent as in [Object#equals].
/// @return an ordered map of native driver/client fields to their associated bindpoints.
Map<String, BindPoint> getFieldBindingsMap();
}

View File

@@ -2,13 +2,13 @@ package io.nosqlbench.adapters.api.activityimpl.uniform;
/*
* Copyright (c) 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
@@ -18,6 +18,8 @@ package io.nosqlbench.adapters.api.activityimpl.uniform;
*/
public interface Validator<RESULT> {
public void validate(RESULT result);
import io.nosqlbench.nb.api.components.core.NBNamedElement;
public interface Validator<RESULT> extends NBNamedElement {
public void validate(long cycle, RESULT result);
}

View File

@@ -2,13 +2,13 @@ package io.nosqlbench.adapters.api.activityimpl.uniform;
/*
* Copyright (c) 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
@@ -18,12 +18,15 @@ package io.nosqlbench.adapters.api.activityimpl.uniform;
*/
import io.nosqlbench.adapters.api.activityimpl.OpLookup;
import io.nosqlbench.adapters.api.templating.ParsedOp;
import io.nosqlbench.nb.api.components.core.NBComponent;
import java.util.List;
import java.util.Optional;
/// A [DriverAdapter] may implement this interface to provide adapter-specific
/// validators.
public interface ValidatorSource {
Optional<Validator> getValidator(String name, ParsedOp pop);
List<Validator> getValidator(NBComponent parent, ParsedOp pop, OpLookup lookup);
}

View File

@@ -32,7 +32,7 @@ public class AssertingOp<T> implements CycleOp<T> {
@Override
public T apply(long value) {
T result = op.apply(value);
validator.validate(result);
validator.validate(value, result);
return result;
}
}

View File

@@ -0,0 +1,55 @@
package io.nosqlbench.adapters.api.activityimpl.uniform.opwrappers;
/*
* Copyright (c) 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.
*/
public enum DiffType {
/// Verify nothing for this statement
none(0),
/// Verify that fields named in the row are present in the reference map.
rowfields(0x1),
/// Verify that fields in the reference map are present in the row data.
reffields(0x1 << 1),
/// Verify that all fields present in either the row or the reference data
/// are also present in the other.
fields(0x1 | 0x1 << 1),
/// Verify that all values of the same named field are equal, according to
/// {@link Object#equals(Object)}}.
values(0x1<<2),
/// Cross-verify all fields and field values between the reference data and
/// the actual data.
all(0x1|0x1<<1|0x1<<2);
public int bitmask;
DiffType(int bit) {
this.bitmask = bit;
}
public boolean is(DiffType option) {
return (bitmask & option.bitmask) > 0;
}
}

View File

@@ -30,6 +30,7 @@ import io.nosqlbench.nb.api.config.fieldreaders.DynamicFieldReader;
import io.nosqlbench.nb.api.config.fieldreaders.StaticFieldReader;
import io.nosqlbench.nb.api.config.standard.NBConfigError;
import io.nosqlbench.nb.api.config.standard.NBConfiguration;
import io.nosqlbench.nb.api.engine.util.Tagged;
import io.nosqlbench.nb.api.errors.OpConfigError;
import io.nosqlbench.nb.api.labels.NBLabelSpec;
import io.nosqlbench.nb.api.labels.NBLabels;
@@ -380,7 +381,7 @@ prepared: false
field within the set of possible fields. More than one will throw an error.</LI>
</UL>
</P> */
public class ParsedOp extends NBBaseComponent implements LongFunction<Map<String, ?>>, NBComponent, StaticFieldReader, DynamicFieldReader {
public class ParsedOp extends NBBaseComponent implements LongFunction<Map<String, ?>>, NBComponent, StaticFieldReader, DynamicFieldReader, Tagged {
private static final Logger logger = LogManager.getLogger(ParsedOp.class);
@@ -418,9 +419,12 @@ public class ParsedOp extends NBBaseComponent implements LongFunction<Map<String
List<Function<Map<String, Object>, Map<String, Object>>> preprocessors,
NBComponent parent
) {
// TODO: the block and op name below should be populated more robustly
// They should not be strictly required, but a way of taking "what is provided" in the
// name should be used
super(
parent,
NBLabels.forKV(((parent instanceof ParsedOp) ? "subop" : "op"), opTemplate.getName())
NBLabels.forMap(opTemplate.getTags())
);
this._opTemplate = opTemplate;
this.activityCfg = activityCfg;
@@ -894,6 +898,7 @@ public class ParsedOp extends NBBaseComponent implements LongFunction<Map<String
return tmap.getOptionalTargetEnum(enumclass, valueClass);
}
public <E extends Enum<E>, V> Optional<TypeAndTarget<E, V>> getOptionalTypeAndTargetEnum(
Class<E> enumclass, Class<V> valueClass) {
return tmap.getOptionalTargetEnum(enumclass, valueClass);
@@ -1025,6 +1030,11 @@ public class ParsedOp extends NBBaseComponent implements LongFunction<Map<String
return this._opTemplate.getRefKey();
}
@Override
public Map<String, String> getTags() {
return this._opTemplate.getTags();
}
public static enum SubOpNaming {
SubKey, ParentAndSubKey
@@ -1046,9 +1056,9 @@ public class ParsedOp extends NBBaseComponent implements LongFunction<Map<String
return new ParsedOp(
new OpData(
"sub-op of '" + this.getName() + "' field '" + fromOpField + "', element '" + elemName + "' name '" + subopName + "'",
subopName, new LinkedHashMap<String, String>(_opTemplate.getTags()) {{
put("subop", subopName);
}}, _opTemplate.getBindings(), _opTemplate.getParams(), opfields, 100
subopName,
new LinkedHashMap<String, String>(Map.of("subop", subopName)),
_opTemplate.getBindings(), _opTemplate.getParams(), opfields, 100
), this.activityCfg, List.of(), this
);
}
@@ -1096,10 +1106,23 @@ public class ParsedOp extends NBBaseComponent implements LongFunction<Map<String
return subOpMap;
}
public ParsedOp takeAsSubConfig(String s) {
Object subtree = tmap.takeStaticValue(s, Object.class);
if (subtree instanceof Map map) {
return makeSubOp(s, s, map, SubOpNaming.SubKey);
} else if (subtree instanceof String seq) {
return makeSubOp(s, s, Map.of(s, seq), SubOpNaming.SubKey);
} else {
throw new RuntimeException(
"unable to make sub config from key '" + s + "', because " + "it is a " + subtree.getClass().getCanonicalName());
}
}
public ParsedOp getAsSubOp(String name, SubOpNaming naming) {
Object o = _opTemplate.getOp().map(raw -> raw.get(name)).orElseThrow(
() -> new OpConfigError(
"Could not find op field '" + name + "' for subop on parent op '" + name + "'"));
if (o instanceof Map map) {
return makeSubOp(this.getName(), name, map, naming);
} else {
@@ -1230,8 +1253,8 @@ public class ParsedOp extends NBBaseComponent implements LongFunction<Map<String
return tmap.getCaptures();
}
public Map<String, String> getBindPoints() {
return null;
public List<BindPoint> getBindPoints() {
return tmap.getBindPoints();
}
public boolean isDefinedExactly(String... fields) {