debugged broken template, improved diagnostics

This commit is contained in:
Jonathan Shook
2020-04-13 14:38:54 -05:00
parent 612413cf09
commit 2e62af2be6
5 changed files with 97 additions and 50 deletions

View File

@@ -138,13 +138,23 @@ public class StdoutActivity extends SimpleActivity implements ActivityDefObserve
String format = getParams().getOptionalString("format").orElse(null); String format = getParams().getOptionalString("format").orElse(null);
if ((stmts.size()==0 && stmtsDocList.getDocBindings().size() > 0) || format!=null) { if ((stmts.size()==0 && stmtsDocList.getDocBindings().size() > 0) || format!=null) {
logger.info("Creating stdout statement template from bindings, since none is otherwise defined."); if (format!=null && format.startsWith("diag")) {
String generatedStmt = genStatementTemplate(stmtsDocList.getDocBindings().keySet()); logger.info("Creating diagnostic log for resolver construction...");
BindingsTemplate bt = new BindingsTemplate(); BindingsTemplate bt = new BindingsTemplate();
stmtsDocList.getDocBindings().forEach(bt::addFieldBinding); stmtsDocList.getDocBindings().forEach(bt::addFieldBinding);
StringBindingsTemplate sbt = new StringBindingsTemplate(generatedStmt, bt); String diagnostics = bt.getDiagnostics();
StringBindings sb = sbt.resolve(); System.out.println(diagnostics);
sequencer.addOp(sb,1L); System.out.flush();
System.exit(2);
} else {
logger.info("Creating stdout statement template from bindings, since none is otherwise defined.");
String generatedStmt = genStatementTemplate(stmtsDocList.getDocBindings().keySet());
BindingsTemplate bt = new BindingsTemplate();
stmtsDocList.getDocBindings().forEach(bt::addFieldBinding);
StringBindingsTemplate sbt = new StringBindingsTemplate(generatedStmt, bt);
StringBindings sb = sbt.resolve();
sequencer.addOp(sb,1L);
}
} else if (stmts.size() > 0) { } else if (stmts.size() > 0) {
for (StmtDef stmt : stmts) { for (StmtDef stmt : stmts) {
ParsedStmt parsed = stmt.getParsed().orError(); ParsedStmt parsed = stmt.getParsed().orError();

View File

@@ -33,9 +33,11 @@ activity types.
- **newline** - whether to automatically add a missing newline to the end - **newline** - whether to automatically add a missing newline to the end
of any statements. of any statements.
default: true default: true
- **format** - which format to use. If provided, the format will override - **format** - which format to use. If provided, the format will override any statement formats provided by the YAML.
any statement formats provided by the YAML. valid values are (csv, readout, json, inlinejson, assignments, and diag)
valid values are (csv, readout, json, inlinejson, and assignments) - When 'format=diag', then the internal construction logic for the binding is logged in detail and nosqlbench exits.
This is useful for detailed diagnostics when you run into trouble, but not generally otherwise. This provides
details that you may include in a bug report if you think there is a bindings bug.
## Configuration ## Configuration

View File

@@ -38,7 +38,7 @@ import java.util.Optional;
* bindings will be used in. * bindings will be used in.
*/ */
public class BindingsTemplate { public class BindingsTemplate {
private final static Logger logger = LogManager.getLogger(BindingsTemplate.class); private final static Logger logger = LogManager.getLogger(BindingsTemplate.class);
private List<String> bindPointNames = new ArrayList<>(); private List<String> bindPointNames = new ArrayList<>();
private List<String> specifiers = new ArrayList<>(); private List<String> specifiers = new ArrayList<>();
@@ -47,11 +47,11 @@ public class BindingsTemplate {
// } // }
public BindingsTemplate(List<String> anchors, List<String> specs) { public BindingsTemplate(List<String> anchors, List<String> specs) {
if (anchors.size()!=specs.size()) { if (anchors.size() != specs.size()) {
throw new InvalidParameterException("Anchors and Specifiers must be matched pair-wise."); throw new InvalidParameterException("Anchors and Specifiers must be matched pair-wise.");
} }
for (int i = 0; i < anchors.size(); i++) { for (int i = 0; i < anchors.size(); i++) {
addFieldBinding(anchors.get(i),specs.get(i)); addFieldBinding(anchors.get(i), specs.get(i));
} }
} }
@@ -64,14 +64,15 @@ public class BindingsTemplate {
public void addFieldBindings(List<BindPoint> bindPoints) { public void addFieldBindings(List<BindPoint> bindPoints) {
for (BindPoint bindPoint : bindPoints) { for (BindPoint bindPoint : bindPoints) {
addFieldBinding(bindPoint.getAnchor(),bindPoint.getBindspec()); addFieldBinding(bindPoint.getAnchor(), bindPoint.getBindspec());
} }
} }
/** /**
* Add a named binding specifier to the template * Add a named binding specifier to the template
*
* @param bindPointName the name associated with the binding specifier * @param bindPointName the name associated with the binding specifier
* @param genSpec the binding specifier * @param genSpec the binding specifier
*/ */
public void addFieldBinding(String bindPointName, String genSpec) { public void addFieldBinding(String bindPointName, String genSpec) {
this.bindPointNames.add(bindPointName); this.bindPointNames.add(bindPointName);
@@ -80,19 +81,39 @@ public class BindingsTemplate {
/** /**
* Add multiple named bindings to the template * Add multiple named bindings to the template
*
* @param bindPairs A map of named binding specifiers * @param bindPairs A map of named binding specifiers
*/ */
public void addFieldBindings(Map<String,String> bindPairs) { public void addFieldBindings(Map<String, String> bindPairs) {
for (Map.Entry<String, String> e : bindPairs.entrySet()) { for (Map.Entry<String, String> e : bindPairs.entrySet()) {
this.bindPointNames.add(e.getKey()); this.bindPointNames.add(e.getKey());
this.specifiers.add(e.getValue()); this.specifiers.add(e.getValue());
} }
} }
public String getDiagnostics() {
StringBuilder diaglog = new StringBuilder();
for (String specifier : specifiers) {
diaglog.append("for ").append(specifier).append(":");
ResolverDiagnostics mapperDiagnostics = VirtData.getMapperDiagnostics(specifier);
String diagnostics = mapperDiagnostics.toString();
diaglog.append(diagnostics);
if (mapperDiagnostics.getResolvedFunction().isPresent()) {
diaglog.append("☑ RESOLVED:")
.append(mapperDiagnostics.getResolvedFunction().get().toString()).append("\n");
} else {
diaglog.append("☐ UNRESOLVED\n");
}
}
return diaglog.toString();
}
/** /**
* Use the data mapping library and the specifier to create instances of data mapping functions. * Use the data mapping library and the specifier to create instances of data mapping functions.
* If you need thread-aware mapping, be sure to call this in the proper thread. Each time this method * If you need thread-aware mapping, be sure to call this in the proper thread. Each time this method
* is called, it creates a new instance. * is called, it creates a new instance.
*
* @return A set of bindings that can be used to yield mapped data values later. * @return A set of bindings that can be used to yield mapped data values later.
*/ */
public Bindings resolveBindings() { public Bindings resolveBindings() {
@@ -104,9 +125,9 @@ public class BindingsTemplate {
} else { } else {
logAvailableDataMappers(); logAvailableDataMappers();
throw new RuntimeException( throw new RuntimeException(
"data mapper binding was unsuccessful for " "data mapper binding was unsuccessful for "
+ ", spec:" + specifier + ", spec:" + specifier
+ ", see log for known data mapper names."); + ", see log for known data mapper names.");
} }
} }
return new Bindings(this, dataMappers); return new Bindings(this, dataMappers);
@@ -149,7 +170,7 @@ public class BindingsTemplate {
sb.append("=>["); sb.append("=>[");
sb.append(values[i]); sb.append(values[i]);
sb.append("]("); sb.append("](");
sb.append((null!=values[i]) ? values[i].getClass().getSimpleName() : "NULL"); sb.append((null != values[i]) ? values[i].getClass().getSimpleName() : "NULL");
sb.append(")"); sb.append(")");
delim = ", "; delim = ", ";
} }

View File

@@ -50,11 +50,11 @@ import java.util.stream.Collectors;
public class VirtDataComposer { public class VirtDataComposer {
private final static String PREAMBLE = "compose "; private final static String PREAMBLE = "compose ";
private final static Logger logger = LogManager.getLogger(DataMapperLibrary.class); private final static Logger logger = LogManager.getLogger(DataMapperLibrary.class);
private final static MethodHandles.Lookup lookup = MethodHandles.publicLookup(); private final static MethodHandles.Lookup lookup = MethodHandles.publicLookup();
private final VirtDataFunctionLibrary functionLibrary; private final VirtDataFunctionLibrary functionLibrary;
private final Map<String,Object> customElements = new HashMap<>(); private final Map<String, Object> customElements = new HashMap<>();
public VirtDataComposer(VirtDataFunctionLibrary functionLibrary) { public VirtDataComposer(VirtDataFunctionLibrary functionLibrary) {
this.functionLibrary = functionLibrary; this.functionLibrary = functionLibrary;
@@ -90,23 +90,22 @@ public class VirtDataComposer {
public ResolverDiagnostics resolveDiagnosticFunctionFlow(VirtDataFlow flow) { public ResolverDiagnostics resolveDiagnosticFunctionFlow(VirtDataFlow flow) {
ResolverDiagnostics diagnostics = new ResolverDiagnostics(); ResolverDiagnostics diagnostics = new ResolverDiagnostics();
diagnostics.trace("processing flow " + flow.toString() + " from output to input");
LinkedList<List<ResolvedFunction>> funcs = new LinkedList<>(); LinkedList<List<ResolvedFunction>> funcs = new LinkedList<>();
LinkedList<Set<Class<?>>> nextFunctionInputTypes = new LinkedList<>(); LinkedList<Set<Class<?>>> nextFunctionInputTypes = new LinkedList<>();
Optional<Class<?>> finalValueTypeOption = Optional<Class<?>> finalValueTypeOption =
Optional.ofNullable(flow.getLastExpression().getCall().getOutputType()) Optional.ofNullable(flow.getLastExpression().getCall().getOutputType())
.map(ValueType::valueOfClassName).map(ValueType::getValueClass); .map(ValueType::valueOfClassName).map(ValueType::getValueClass);
nextFunctionInputTypes.add(new HashSet<>()); nextFunctionInputTypes.add(new HashSet<>());
finalValueTypeOption.ifPresent(t -> nextFunctionInputTypes.get(0).add(t)); finalValueTypeOption.ifPresent(t -> nextFunctionInputTypes.get(0).add(t));
diagnostics.trace("working backwards from " + (flow.getExpressions().size()-1)); diagnostics.trace("working backwards from index " + (flow.getExpressions().size() - 1) + " to index 0");
for (int i = flow.getExpressions().size() - 1; i >= 0; i--) { for (int i = flow.getExpressions().size() - 1; i >= 0; i--) {
FunctionCall call = flow.getExpressions().get(i).getCall(); FunctionCall call = flow.getExpressions().get(i).getCall();
diagnostics.trace("resolving args for " + call.toString()); diagnostics.trace("FUNCTION[" + i + "]: " + call.toString() + ", resolving args");
// diagnostics.trace("resolving args for " + call.toString());
List<ResolvedFunction> nodeFunctions = new LinkedList<>(); List<ResolvedFunction> nodeFunctions = new LinkedList<>();
@@ -114,23 +113,24 @@ public class VirtDataComposer {
Class<?> inputType = ValueType.classOfType(call.getInputType()); Class<?> inputType = ValueType.classOfType(call.getInputType());
Class<?> outputType = ValueType.classOfType(call.getOutputType()); Class<?> outputType = ValueType.classOfType(call.getOutputType());
Object[] args = call.getArguments(); Object[] args = call.getArguments();
try { try {
args = populateFunctions(diagnostics, args, this.customElements); args = populateFunctions(diagnostics, args, this.customElements);
} catch (Exception e) { } catch (Exception e) {
return diagnostics.error(e); return diagnostics.error(e);
} }
diagnostics.trace("resolved args: "); diagnostics.trace(" resolved args:");
for (Object arg : args) { for (Object arg : args) {
diagnostics.trace(" " + arg.getClass().getSimpleName() + ": " + arg.toString()); diagnostics.trace(" " + arg.getClass().getSimpleName() + ": " + arg.getClass().getCanonicalName());
} }
List<ResolvedFunction> resolved = functionLibrary.resolveFunctions(outputType, inputType, funcName, this.customElements,args); List<ResolvedFunction> resolved = functionLibrary.resolveFunctions(outputType, inputType, funcName, this.customElements, args);
if (resolved.size() == 0) { if (resolved.size() == 0) {
return diagnostics.error(new RuntimeException("Unable to find even one function for " + call)); return diagnostics.error(new RuntimeException("Unable to find even one function for " + call));
} }
diagnostics.trace(" resolved functions:"); diagnostics.trace(" resolved functions");
diagnostics.trace(summarize(resolved)); diagnostics.trace(summarize(resolved, " - "));
nodeFunctions.addAll(resolved); nodeFunctions.addAll(resolved);
funcs.addFirst(nodeFunctions); funcs.addFirst(nodeFunctions);
@@ -152,10 +152,10 @@ public class VirtDataComposer {
} }
FunctionAssembly assembly = new FunctionAssembly(); FunctionAssembly assembly = new FunctionAssembly();
diagnostics.trace("composed summary: " + summarize(flattenedFuncs)); // diagnostics.trace("composed summary: " + summarize(flattenedFuncs));
boolean isThreadSafe = true; boolean isThreadSafe = true;
diagnostics.trace("FUNCTION chain selected: (multi) '" + this.summarize(flattenedFuncs) + "'"); diagnostics.trace("FUNCTION chain selected: (multi) '" + this.summarize(flattenedFuncs, " - ") + "'");
for (ResolvedFunction resolvedFunction : flattenedFuncs) { for (ResolvedFunction resolvedFunction : flattenedFuncs) {
try { try {
Object functionObject = resolvedFunction.getFunctionObject(); Object functionObject = resolvedFunction.getFunctionObject();
@@ -164,7 +164,7 @@ public class VirtDataComposer {
isThreadSafe = false; isThreadSafe = false;
} }
} catch (Exception e) { } catch (Exception e) {
String flowdata = flow!=null? flow.toString() : "undefined"; String flowdata = flow != null ? flow.toString() : "undefined";
return diagnostics.error(new RuntimeException("FUNCTION resolution failed: '" + flowdata + "': " + e.toString())); return diagnostics.error(new RuntimeException("FUNCTION resolution failed: '" + flowdata + "': " + e.toString()));
} }
} }
@@ -178,7 +178,7 @@ public class VirtDataComposer {
return resolverDiagnostics.getResolvedFunction(); return resolverDiagnostics.getResolvedFunction();
} }
private Object[] populateFunctions(ResolverDiagnostics diagnostics, Object[] args, Map<String,?> cconfig) { private Object[] populateFunctions(ResolverDiagnostics diagnostics, Object[] args, Map<String, ?> cconfig) {
for (int i = 0; i < args.length; i++) { for (int i = 0; i < args.length; i++) {
Object o = args[i]; Object o = args[i];
if (o instanceof FunctionCall) { if (o instanceof FunctionCall) {
@@ -188,7 +188,8 @@ public class VirtDataComposer {
Class<?> inputType = ValueType.classOfType(call.getInputType()); Class<?> inputType = ValueType.classOfType(call.getInputType());
Class<?> outputType = ValueType.classOfType(call.getOutputType()); Class<?> outputType = ValueType.classOfType(call.getOutputType());
Object[] fargs = call.getArguments(); Object[] fargs = call.getArguments();
diagnostics.trace("resolving argument as function '" + call.toString() + "'"); diagnostics.trace(" arg (function): " + call.toString());
// diagnostics.trace("resolving argument as function '" + call.toString() + "'");
fargs = populateFunctions(diagnostics, fargs, cconfig); fargs = populateFunctions(diagnostics, fargs, cconfig);
List<ResolvedFunction> resolved = functionLibrary.resolveFunctions(outputType, inputType, funcName, cconfig, fargs); List<ResolvedFunction> resolved = functionLibrary.resolveFunctions(outputType, inputType, funcName, cconfig, fargs);
@@ -215,9 +216,11 @@ public class VirtDataComposer {
funcs.removeAll(toRemove); funcs.removeAll(toRemove);
} }
private String summarize(List<ResolvedFunction> funcs) { private String summarize(List<ResolvedFunction> funcs, String prefix) {
return funcs.stream() return funcs.stream()
.map(String::valueOf).collect(Collectors.joining("|")); .map(String::valueOf)
.map(s -> prefix + s)
.collect(Collectors.joining("\n"));
} }
private String summarizeBulk(List<List<ResolvedFunction>> funcs) { private String summarizeBulk(List<List<ResolvedFunction>> funcs) {
@@ -225,9 +228,9 @@ public class VirtDataComposer {
List<List<String>> spans = new LinkedList<>(); List<List<String>> spans = new LinkedList<>();
funcs.forEach(l -> spans.add(l.stream().map(String::valueOf).collect(Collectors.toList()))); funcs.forEach(l -> spans.add(l.stream().map(String::valueOf).collect(Collectors.toList())));
List<Optional<Integer>> widths = spans.stream().map( List<Optional<Integer>> widths = spans.stream().map(
l -> l.stream().map(String::length).max(Integer::compare)).collect(Collectors.toList()); l -> l.stream().map(String::length).max(Integer::compare)).collect(Collectors.toList());
String funcsdata = spans.stream().map( String funcsdata = spans.stream().map(
l -> l.stream().map(String::valueOf).collect(Collectors.joining("|\n")) l -> l.stream().map(String::valueOf).collect(Collectors.joining("|\n"))
).collect(Collectors.joining("\n\n")); ).collect(Collectors.joining("\n\n"));
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
@@ -321,7 +324,7 @@ public class VirtDataComposer {
funcs.sort(ResolvedFunction.PREFERRED_TYPE_COMPARATOR); funcs.sort(ResolvedFunction.PREFERRED_TYPE_COMPARATOR);
while (funcs.size() > 1) { while (funcs.size() > 1) {
logger.trace("BY-SINGLE-PREFERRED-TYPE removing func " + funcs.get(funcs.size() - 1) logger.trace("BY-SINGLE-PREFERRED-TYPE removing func " + funcs.get(funcs.size() - 1)
+ " because " + funcs.get(0) + " has more preferred types."); + " because " + funcs.get(0) + " has more preferred types.");
funcs.remove(funcs.size() - 1); funcs.remove(funcs.size() - 1);
} }
@@ -336,7 +339,7 @@ public class VirtDataComposer {
prevFuncs.sort(ResolvedFunction.PREFERRED_TYPE_COMPARATOR); prevFuncs.sort(ResolvedFunction.PREFERRED_TYPE_COMPARATOR);
while (prevFuncs.size() > 1) { while (prevFuncs.size() > 1) {
String logmsg = "BY-PREV-PREFERRED-TYPE removing func " + prevFuncs.get(prevFuncs.size() - 1) String logmsg = "BY-PREV-PREFERRED-TYPE removing func " + prevFuncs.get(prevFuncs.size() - 1)
+ " because " + prevFuncs.get(0) + " has more preferred types."; + " because " + prevFuncs.get(0) + " has more preferred types.";
logger.trace(logmsg); logger.trace(logmsg);
prevFuncs.remove(prevFuncs.size() - 1); prevFuncs.remove(prevFuncs.size() - 1);
} }
@@ -345,7 +348,7 @@ public class VirtDataComposer {
nextFuncs.sort(ResolvedFunction.PREFERRED_TYPE_COMPARATOR); nextFuncs.sort(ResolvedFunction.PREFERRED_TYPE_COMPARATOR);
while (nextFuncs.size() > 1) { while (nextFuncs.size() > 1) {
String logmsg = "BY-NEXT-PREFERRED-TYPE removing func " + nextFuncs.get(nextFuncs.size() - 1) String logmsg = "BY-NEXT-PREFERRED-TYPE removing func " + nextFuncs.get(nextFuncs.size() - 1)
+ " because " + nextFuncs.get(0) + " has more preferred types."; + " because " + nextFuncs.get(0) + " has more preferred types.";
logger.trace(logmsg); logger.trace(logmsg);
nextFuncs.remove(nextFuncs.size() - 1); nextFuncs.remove(nextFuncs.size() - 1);
} }
@@ -369,7 +372,7 @@ public class VirtDataComposer {
Set<Class<?>> outputs = getOutputs(prevFuncs); Set<Class<?>> outputs = getOutputs(prevFuncs);
Set<Class<?>> inputs = getInputs(nextFuncs); Set<Class<?>> inputs = getInputs(nextFuncs);
Set<Class<?>> directMatches = Set<Class<?>> directMatches =
inputs.stream().filter(outputs::contains).collect(Collectors.toCollection(HashSet::new)); inputs.stream().filter(outputs::contains).collect(Collectors.toCollection(HashSet::new));
if (directMatches.size() > 0) { if (directMatches.size() > 0) {
List<ResolvedFunction> toremove = new ArrayList<>(); List<ResolvedFunction> toremove = new ArrayList<>();
@@ -423,9 +426,9 @@ public class VirtDataComposer {
return 0; return 0;
} else { } else {
toremove.forEach(nextfunc -> { toremove.forEach(nextfunc -> {
String logmsg = "BY-ASSIGNABLE-TYPE removing next func: " + nextfunc + " because its input types are not assignable from any of the previous funcs"; String logmsg = "BY-ASSIGNABLE-TYPE removing next func: " + nextfunc + " because its input types are not assignable from any of the previous funcs";
logger.trace(logmsg); logger.trace(logmsg);
} }
); );
nextFuncs.removeAll(toremove); nextFuncs.removeAll(toremove);
@@ -450,9 +453,10 @@ public class VirtDataComposer {
return inputs; return inputs;
} }
public Map<String,?> getCustomElements() { public Map<String, ?> getCustomElements() {
return this.customElements; return this.customElements;
} }
public VirtDataComposer addCustomElement(String name, Object element) { public VirtDataComposer addCustomElement(String name, Object element) {
this.customElements.put(name, element); this.customElements.put(name, element);
return this; return this;

View File

@@ -34,4 +34,14 @@ public class StringBindingsTemplate {
Bindings bindings = bindingsTemplate.resolveBindings(); Bindings bindings = bindingsTemplate.resolveBindings();
return new StringBindings(compositor,bindings); return new StringBindings(compositor,bindings);
} }
public String getDiagnostics() {
StringCompositor compositor = new StringCompositor(stringTemplate);
HashSet<String> unqualifiedNames = new HashSet<>(compositor.getBindPointNames());
unqualifiedNames.removeAll(new HashSet<>(bindingsTemplate.getBindPointNames()));
if (unqualifiedNames.size()>0) {
throw new RuntimeException("Named anchors were specified in the template which were not provided in the bindings: " + unqualifiedNames.toString());
}
return bindingsTemplate.getDiagnostics();
}
} }