From a06b62c12dd8a250056e2af1bab92d5471b8104a Mon Sep 17 00:00:00 2001
From: Jonathan Shook
Date: Fri, 26 Jul 2024 16:57:40 -0500
Subject: [PATCH 1/7] fix broken docs formatting and provide uniform method for
multiple names
---
.../adapters/api/templating/ParsedOp.java | 279 +++++++++---------
1 file changed, 138 insertions(+), 141 deletions(-)
diff --git a/nb-apis/adapters-api/src/main/java/io/nosqlbench/adapters/api/templating/ParsedOp.java b/nb-apis/adapters-api/src/main/java/io/nosqlbench/adapters/api/templating/ParsedOp.java
index 04de0e633..1d7f43595 100644
--- a/nb-apis/adapters-api/src/main/java/io/nosqlbench/adapters/api/templating/ParsedOp.java
+++ b/nb-apis/adapters-api/src/main/java/io/nosqlbench/adapters/api/templating/ParsedOp.java
@@ -70,7 +70,7 @@ import java.util.function.LongFunction;
* specification tests under the workload_definition folder of the adapters-api module.
*
*
- *
+ *
* - Rule #1: All op templates are parsed into an internal normalized structure which contains:
@@ -92,36 +92,41 @@ import java.util.function.LongFunction;
* The net effect of these rules is that the NoSQLBench driver developer may safely use functional forms to access data
* in the op template, or may decide that certain op fields must only be provided in a static way per operation.
*
- *
+ *
* Distinguishing Op Payload from Op Config
* When a user specifies an op template, they may choose to provide only a single set of op fields without
* distinguishing between config or payload, or they may choose to directly configure each.
- *
Example:
+ *
* {@code
* ops:
- * # both op and params explicitly named
- * op1:
- * op:
- * opfield1: value1
- * params:
- * param2: value2
- * # neither op field nor params named, so all assumed to be op fields
- * op2:
- * opfield1: value1
- * param2: value2
- * # in this case, if param2 is meant to be config level,
- * # it is a likely config error that should be thrown to the user
- * # only op field explicitly named, so remainder automatically pushed into params
- * op3:
- * op:
- * opfield1: value1
- * param2: value2
- * # only params explicitly named, so remainder pushed into op payload
- * op4:
- * params:
- * param2: value2
- * opfield1: value1
- * }
+ *
+ * # both op and params explicitly named
+ * op1:
+ * op:
+ * opfield1: value1
+ * params:
+ * param2: value2
+ *
+ * # neither op field nor params named, so all assumed to be op fields
+ * op2:
+ * opfield1: value1
+ * param2: value2
+ *
+ * # in this case, if param2 is meant to be config level,
+ * # it is a likely config error that should be thrown to the user
+ * # only op field explicitly named, so remainder automatically pushed into params
+ * op3:
+ * op:
+ * opfield1: value1
+ * param2: value2
+ *
+ * # only params explicitly named, so remainder pushed into op payload
+ * op4:
+ * params:
+ * param2: value2
+ * opfield1: value1
+ * }
+ *
*
* All of these are considered valid constructions, and all of them may actually achieve the same result.
* This looks like an undesirable problem, but it serves to simplify things for users in one specific way: It allows
@@ -135,37 +140,37 @@ import java.util.function.LongFunction;
*
* Design Invariants
*
- * The above rules imply invariants, which are made explicit here. {@link ParsedOp}.
+ * The above rules imply invariants, which are made explicit here.
*
- *
+ * Single Purpose Fields
+ * You may not use an op field name or parameter name for more than one purpose.
*
- * - You may not use an op field name or parameter name for more than one purpose.
- *
- * - Treat all parameters supported by a driver adapter and it's op fields as a globally shared namespace, even if it
- * is not.
- * This avoids creating any confusion about what a parameter can be used for and how to use it for the right thing in
- * the right place.
- * For example, you may not use the parameter name `socket` in an op template to mean one thing and then use it
+ *
Shared Namespace
+ * Treat all parameters supported by a driver adapter and it's op fields as a globally shared namespace, even
+ * if it is not.
+ *
+ * This avoids creating any confusion about what a parameter can be used for and how to use it for the right thing in
+ * the right place. For example, you may not use the parameter name `socket` in an op template to mean one thing and then use it
* at the driver adapter level to mean something different. However, if the meaning is congruent, a driver developer
* may choose to support some cross-cutting parameters at the activity level. These allowances are explicit,
- * however, as each driver dictates what it will allow as activity parameters.
- *
- *
- *
+ * however, as each driver dictates what it will allow as activity parameters.
+ *
+ * Layered Resolution
+ *
+ * Users may specify op payload fields within op params or activity params as fallback config sources in that
+ * order.
*
- * - Users may specify op payload fields within op params or activity params as fallback config sources in that
- * order.
*
* - IF a name is valid as an op field, it must also be valid as such when specified in op params.
* - If a name is valid as an op field, it must also be valid as such when specified in activity params, within the
* scope of {@link ParsedOp}
* - When an op field is found via op params or activity params, it may NOT be dynamic. If dynamic values are intended
- * to be provided
- * at a common layer in the workload, then bindings support this already.
+ * to be provided at a common layer in the workload, then bindings or template variables support this already.
*
*
*
- * - You must access non-payload params via Config-oriented methods.
+ *
Configuration Fields
+ * You must access non-payload params via Config-oriented methods.
*
* - Op Templates contain op payload data and op configs (params, activity params).
* - You must use only {@link ParsedOp} getters with "...Config..." names, such as
@@ -176,56 +181,49 @@ import java.util.function.LongFunction;
*
*
*
- * - The user must be warned when a required or optional config value is missing from op params (or activity
- * params), but a value
- * of the same name is found in op payload fields.
- *
- * - If rule #1 is followed, and names are unambiguous across the driver, then it is almost certainly a configuration
- * error.
- *
- *
+ * Sanity Checks
*
- * - When both an op payload field and a param field of the same name are defined through cascading configuration
- * of param fields,
- * the local op payload field takes precedence.
+ *
The user must be warned when a required or optional config value is missing from op params (or activity
+ * params), but a value of the same name is found in op payload fields.
+ * If rule #1 is followed, and names are unambiguous across the driver, then it is almost certainly a configuration
+ * error.
+ *
+ * Precedence
+ * The order of precedence for values is:
*
- * - This is an extension of the param override rules which say that the closest (most local) value to an operation is
- * the one that takes precedence.
- * - In practice, there will be no conflicts between direct static and dynamic fields, but there will be possibly
- * between
- * static or dynamic fields and parameters and activity params. If a user wants to promote an activity param as an
- * override to existing op fields,
- * template variables allow for this to happen gracefully. Otherwise, the order of precedence is 1) op fields 2) op
- * params 3) activity params.
- *
- *
+ * - op payload fields
+ * - op param fields
+ * - activity params (when enabled, see below)
*
*
*
+ * Enabling Activity Params
+ * If a user wants to allow an activity param as an default for an fields, they must publish the op field
+ * name in the configuration model for the activity. Otherwise it is an error to specify the value at the activity
+ * level.
+ *
*
*
* Op Payload Forms
* Field values can come from multiple sources. These forms and any of their combinations are supported.
*
* Static Op Fields
- * Example:
* {@code
* op:
- * field1: value1
- * field2:
- * map3:
- * key4: value4
- * map5:
- * key6: value6
- * field7: false
- * field8: 8.8
+ * field1: value1
+ * field2:
+ * map3:
+ * key4: value4
+ * map5:
+ * key6: value6
+ * field7: false
+ * field8: 8.8
* }
*
* As shown, any literal value of any valid YAML type, including structured values like lists or maps are accepted as
* static op template values. A static value is any value which contains zero bind points at any level.
*
*
Dynamic Op Fields with Binding References
- * Example:
* {@code
* op:
* field1: "{binding1}"
@@ -247,34 +245,32 @@ import java.util.function.LongFunction;
* null values invalid for ANY op template value.
*
* Dynamic Op Fields with Binding Definitions
- * Example:
- *
* {@code
* op:
- * field1: "{{NumberNameToString()}}"
- * field2: "value is: {{NumberNameToString()}}"
+ * field1: "{{NumberNameToString()}}"
+ * field2: "value is: {{NumberNameToString()}}"
* }
*
*
* This form has exactly the same effect as the previous example as long as your bindings definitions included:
*
{@code
* bindings:
- * binding1: NumberNameToString();
+ * binding1: NumberNameToString();
* }
*
* Dynamic Op Fields with Structure
- * Example:
*
* {@code
- * field1:
- * k1: "{binding1}
- * k2: "literal value"
- * field2:
- * - "value3"
- * - "{binding4}"
- * - "a value: {binding5}"
- * - "{{NumberNameToString}}"
- * - "a value: {{NumberNameToString()}}"
+ * op:
+ * field1:
+ * k1: "{binding1}
+ * k2: "literal value"
+ * field2:
+ * - "value3"
+ * - "{binding4}"
+ * - "a value: {binding5}"
+ * - "{{NumberNameToString}}"
+ * - "a value: {{NumberNameToString()}}"
* }
*
* This example combines the previous ones with structure and dynamic values. Both field1 and field2 are dynamic,
@@ -286,15 +282,14 @@ import java.util.function.LongFunction;
* configure your binding definitions thusly.
*
* Op Template Params
- * Example:
* {@code
* params:
- * prepared: true
+ * prepared: true
* ops:
- * op1:
- * field1: value1
- * params:
- * prepared: false
+ * op1:
+ * field1: value1
+ * params:
+ * prepared: false
* }
*
* The params section are the first layer of external configuration values that an op template can use to distinguish
@@ -308,7 +303,6 @@ import java.util.function.LongFunction;
* down to each block and then down to each statement.
*
*
Activity Params
- * Example:
* {@code
* ./nb run driver=... workload=... cl=LOCAL_QUORUM
* }
@@ -317,6 +311,19 @@ import java.util.function.LongFunction;
* another fallback source for configuration parameters. The {@link ParsedOp} implementation will automatically look
* in the activity parameters if needed to find a missing configuration parameter, but this will only work if
* the specific named parameter is allowed at the activity level.
+ *
+ *
+ * Alternate Names
+ * Sometimes you may need to support more than one name for the same purpose. In such cases, there are helper
+ * methods which can be used to reduce from a set of possible field names to a single one.
+ *
+ * - {@link #requiredFieldOf(String...)} and {@link #requiredFieldOf(List)} will find exactly one field within the set of
+ * possible fields.
+ * Zero or more than one will throw an error.
+ * - {@link #optionalFieldOf(String...)} and {@link #optionalFieldOf(List)} will find exactly zero or one
+ * field within the set of possible fields. More than one will throw an error.
+ *
+ *
*/
public class ParsedOp extends NBBaseComponent implements LongFunction