remainder of changes for scenario rework that need to be itemized

This commit is contained in:
Jonathan Shook
2023-12-06 14:26:53 -06:00
parent a2ac3e3f75
commit ae19037bce
398 changed files with 6764 additions and 10589 deletions

View File

@@ -1,202 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.

View File

@@ -14,8 +14,7 @@
~ limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
@@ -30,18 +29,14 @@
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<description>
Runtime artifact for nosqlbench;
This module ties the core libraries, provided drivers, and API into a single executable jar
The engine API for nosqlbench;
Provides the interfaces needed to build internal modules for the
nosqlbench core engine
</description>
<dependencies>
<dependency>
<groupId>io.nosqlbench</groupId>
<artifactId>engine-api</artifactId>
<version>${revision}</version>
</dependency>
<!-- from core -->
<dependency>
<groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-core</artifactId>
@@ -58,21 +53,7 @@
<artifactId>metrics-graphite</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- graalvm -->
<dependency>
<groupId>org.graalvm.js</groupId>
<artifactId>js</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.graalvm.js</groupId>
<artifactId>js-scriptengine</artifactId>
@@ -83,16 +64,118 @@
<scope>runtime</scope>
</dependency>
<!-- only compile scope -->
<!-- ^ from core -->
<dependency>
<groupId>io.nosqlbench</groupId>
<artifactId>nb-api</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>io.nosqlbench</groupId>
<artifactId>adapters-api</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>io.nosqlbench</groupId>
<artifactId>nb-spectest</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>io.nosqlbench</groupId>
<artifactId>nb-annotations</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>io.nosqlbench</groupId>
<artifactId>virtdata-userlibs</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>org.graalvm.js</groupId>
<artifactId>js</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
</dependency>
<!--<dependency>-->
<!--<groupId>io.dropwizard.metrics</groupId>-->
<!--<artifactId>metrics-jmx</artifactId>-->
<!--<version>${metrics-version}</version>-->
<!--</dependency>-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-handler</artifactId>
</dependency>
<!-- test scope only -->
</dependencies>
<profiles>
<profile>
<id>perftests</id>
<activation>
<activeByDefault>false</activeByDefault>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<groups>perf</groups>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<testResources>
<testResource>
<directory>src/test/resources</directory>
<filtering>false</filtering> <!-- exclusion from defaults -->
</testResource>
<testResource>
<filtering>true</filtering>
<directory>src/test/resources</directory>
<includes>
<include>log4j2-test.xml</include>
</includes>
</testResource>
</testResources>
</build>
</project>

View File

@@ -0,0 +1,66 @@
/*
* 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.engine.api.scenarios;
import io.nosqlbench.nb.api.errors.BasicError;
final class SCNamedParam {
private final String name;
private final String operator;
private final String value;
private String scenarioName;
public SCNamedParam(String name, String operator, String value) {
this.name = name;
this.operator = operator;
this.value = value;
}
public boolean isReassignable() {
return NBCLIScenarioPreprocessor.UNLOCKED.equals(operator);
}
public boolean isFinalSilent() {
return NBCLIScenarioPreprocessor.SILENT_LOCKED.equals(operator);
}
public boolean isFinalVerbose() {
return NBCLIScenarioPreprocessor.VERBOSE_LOCKED.equals(operator);
}
public SCNamedParam override(String value) {
if (isReassignable()) {
return new SCNamedParam(this.name, this.operator, value);
} else if (isFinalSilent()) {
return this;
} else if (isFinalVerbose()) {
throw new BasicError("Unable to reassign value for locked param '" + name + operator + value + "'");
} else {
throw new RuntimeException("impossible!");
}
}
@Override
public String toString() {
return name + (operator != null ? "=" : "") + (value != null ? value : "");
}
public String getName() {
return name;
}
}

View File

@@ -1,234 +0,0 @@
/*
* 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.
* 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.engine.cli;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import java.security.InvalidParameterException;
import java.util.*;
import java.util.function.Function;
/**
* Encapsulate Command parsing and structure for the NoSQLBench command line. Commands always have a name, sometimes
* have a list of positional arguments, and sometimes have a map of named parameters. An example of a command tha thas
* both would look like {@code script test.js p1=v1}
*/
public class Cmd {
private final static Logger logger = LogManager.getLogger(Cmd.class);
public enum CmdType {
run(),
start(),
stop(Arg.of("alias_name")),
forceStop(Arg.of("alias_name")),
script(Arg.of("script_path", s -> s)),
java(Arg.of("main_class",s->s)),
await(Arg.of("alias_name")),
waitMillis(Arg.of("millis_to_wait", Long::parseLong)),
fragment(Arg.ofFreeform("script_fragment")),;
private final Arg<?>[] positional;
CmdType(Arg<?>... positional) {
this.positional = positional;
}
public String[] getPositionalArgNames() {
String[] names = new String[positional.length];
for (int i = 0; i < names.length; i++) {
names[i] = positional[i].name;
}
return names;
}
public static CmdType valueOfAnyCase(String cmdname) {
for (CmdType value : values()) {
if (cmdname.equals(value.toString()) || cmdname.equalsIgnoreCase(value.toString())) {
return value;
}
}
return valueOf(cmdname); // let the normal exception take over in this case
}
public Arg<?>[] getPositionalArgs() {
return positional;
}
}
private static final class Arg<T> {
public final String name;
public final Function<String, T> converter;
public final boolean freeform;
public Arg(String name, Function<String, T> converter, boolean freeform) {
this.name = name;
this.converter = converter;
this.freeform = freeform;
}
public static <T> Arg<T> of(String name, Function<String, T> converter) {
return new Arg<>(name, converter, false);
}
public static Arg<String> of(String name) {
return new Arg<>(name, s -> s, false);
}
public static Arg<String> ofFreeform(String name) {
return new Arg<>(name, s -> s, true);
}
}
private final Map<String, String> cmdArgs;
public String getArg(String paramName) {
return this.cmdArgs.get(paramName);
}
private final CmdType cmdType;
public Cmd(CmdType cmdType, Map<String, String> cmdArgs) {
this.cmdArgs = cmdArgs;
this.cmdType = cmdType;
}
public CmdType getCmdType() {
return cmdType;
}
public Map<String, String> getParams() {
return cmdArgs;
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(cmdType.toString());
sb.append("(");
if (getParams().size() > cmdType.positional.length) {
sb.append(toJSONBlock(getParams(), false));
} else {
for (String value : getParams().values()) {
String trimmed = ((value.startsWith("'") && value.endsWith("'"))
|| (value.startsWith("\"")) && value.endsWith("\"")) ?
value.substring(1, value.length() - 1) : value;
sb.append("'").append(trimmed).append("'").append(",");
}
sb.setLength(sb.length() - 1);
}
sb.append(");");
return sb.toString();
}
public static Cmd parseArg(LinkedList<String> arglist, PathCanonicalizer fixer) {
String cmdName = arglist.removeFirst();
CmdType cmdType = CmdType.valueOfAnyCase(cmdName);
Map<String, String> params = new LinkedHashMap<>();
for (Arg<?> arg : cmdType.getPositionalArgs()) {
String nextarg = arglist.peekFirst();
if (nextarg == null) {
throw new InvalidParameterException(
"command '" + cmdName + " requires a value for " + arg.name
+ ", but there were no remaining arguments after it.");
} else if (arg.freeform) {
logger.debug(() -> "freeform parameter:" + nextarg);
} else if (nextarg.contains("=")) {
throw new InvalidParameterException(
"command '" + cmdName + "' requires a value for " + arg.name +
", but a named parameter was found instead: " + nextarg);
} else if (SessionCommandParser.RESERVED_WORDS.contains(nextarg)) {
throw new InvalidParameterException(
"command '" + cmdName + "' requires a value for " + arg.name
+ ", but a reserved word was found instead: " + nextarg);
}
logger.debug(() -> "cmd name:" + cmdName + ", positional " + arg.name + ": " + nextarg);
params.put(arg.name, arg.converter.apply(arglist.removeFirst()).toString());
}
while (arglist.size() > 0 &&
!SessionCommandParser.RESERVED_WORDS.contains(arglist.peekFirst())
&& arglist.peekFirst().contains("=")) {
String arg = arglist.removeFirst();
String[] assigned = arg.split("=", 2);
String pname = assigned[0];
String pval = assigned[1];
if (pname.equals("yaml") || pname.equals("workload")) {
pval = fixer.canonicalizePath(pval);
}
if (params.containsKey(pname)) {
throw new InvalidParameterException("parameter '" + pname + "' is already set for '" + cmdType +"' command. For each command," +
" a named parameter may only be set once. Multiple occurrences are disallowed to avoid errors or ambiguity.");
}
params.put(pname, pval);
}
return new Cmd(cmdType, params);
}
public static String toJSONBlock(Map<String, String> map, boolean oneline) {
int klen = map.keySet().stream().mapToInt(String::length).max().orElse(1);
StringBuilder sb = new StringBuilder();
List<String> l = new ArrayList<>();
for (Map.Entry<String, String> entries : map.entrySet()) {
String key = entries.getKey();
String value = sanitizeQuotes(entries.getValue());
if (oneline) {
l.add("'" + key + "':'" + value + "'");
} else {
l.add(" '" + key + "': " + " ".repeat(klen - key.length()) + "'" + value + "'");
}
}
return "{" + (oneline ? "" : "\n") + String.join(",\n", l) + (oneline ? "}" : "\n}");
}
private static String sanitizeQuotes(String value) {
if (value.startsWith("'") && value.endsWith("'")) {
return value.substring(1, value.length() - 1);
}
if (value.startsWith("\"") && value.endsWith("\"")) {
return value.substring(1, value.length() - 1);
}
return value;
}
public static String toJSONParams(String varname, Map<String, String> map, boolean oneline) {
return "// params.size==" + map.size() + "\n" + varname + "=" + toJSONBlock(map, oneline);
}
public static List<Cmd> parseCmds (String...arglist){
LinkedList<String> ll = new LinkedList<>(Arrays.asList(arglist));
List<Cmd> cmds = new ArrayList<>();
while (!ll.isEmpty()) {
Cmd cmd = parseArg(ll, null);
cmds.add(cmd);
}
return cmds;
}
}

View File

@@ -1,114 +0,0 @@
/*
* 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.
* 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.engine.cli;
import io.nosqlbench.api.content.Content;
import io.nosqlbench.api.content.NBIO;
import io.nosqlbench.engine.api.scenarios.NBCLIScenarioParser;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.*;
/**
* This parser will return a non-empty optional if there is no error.
* If the optional is empty, then it means some part of the command structure
* was not recognized.
*/
public class SessionCommandParser {
// private final static Logger logger = LogManager.getLogger(SessionCommandParser.class);
private static final String FRAGMENT = "fragment";
private static final String SCRIPT = "script";
private static final String START = "start";
private static final String RUN = "run";
private static final String AWAIT = "await";
private static final String STOP = "stop";
private static final String FORCE_STOP = "forceStop";
private static final String ACTIVITY = "activity";
private static final String SCENARIO = "scenario";
private static final String WAIT_MILLIS = "waitmillis";
private static final String JAVA_MAIN = "java";
public static final Set<String> RESERVED_WORDS = new HashSet<>() {{
addAll(
Arrays.asList(
FRAGMENT, SCRIPT, START, RUN, AWAIT, STOP, FORCE_STOP, ACTIVITY, SCENARIO, WAIT_MILLIS
)
);
}};
public static Optional<List<Cmd>> parse(
LinkedList<String> arglist,
String... includes
) {
boolean scriptCommands = false;
boolean javaCommands = false;
List<Cmd> cmdList = new LinkedList<>();
PathCanonicalizer canonicalizer = new PathCanonicalizer(includes);
while (arglist.peekFirst() != null) {
String word = arglist.peekFirst();
Cmd cmd;
switch (word) {
case JAVA_MAIN:
cmd = Cmd.parseArg(arglist, canonicalizer);
cmdList.add(cmd);
javaCommands = true;
break;
case FRAGMENT:
case SCRIPT:
case START:
case RUN:
case AWAIT:
case STOP:
case FORCE_STOP:
case WAIT_MILLIS:
cmd = Cmd.parseArg(arglist, canonicalizer);
cmdList.add(cmd);
scriptCommands = true;
break;
default:
Optional<Content<?>> scriptfile = NBIO.local()
.searchPrefixes("scripts/auto")
.pathname(word)
.extensionSet("js")
.first();
//Script
if (scriptfile.isPresent()) {
arglist.removeFirst();
arglist.addFirst("scripts/auto/" + word);
arglist.addFirst("script");
cmd = Cmd.parseArg(arglist, canonicalizer);
cmdList.add(cmd);
} else if (NBCLIScenarioParser.isFoundWorkload(word, includes)) {
NBCLIScenarioParser.parseScenarioCommand(arglist, RESERVED_WORDS, includes);
} else {
System.out.println("unrecognized Cmd: " + word); // instead of using logger due to init precedence
return Optional.empty();
}
break;
}
if (javaCommands && scriptCommands) {
throw new RuntimeException("combining java and javascript commands into one session is not yet supported.");
}
}
return Optional.of(cmdList);
}
}

View File

@@ -0,0 +1,262 @@
/*
* 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.
* 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.engine.cmdstream;
import io.nosqlbench.engine.core.lifecycle.session.CmdParser;
import io.nosqlbench.nb.api.errors.BasicError;
import jakarta.validation.constraints.NotNull;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import java.security.InvalidParameterException;
import java.util.*;
/**
* Encapsulate Command parsing and structure for the NoSQLBench command line. Commands always have a name, sometimes
* have a list of positional arguments, and sometimes have a map of named parameters. An example of a command tha thas
* both would look like {@code script test.js p1=v1}
*/
public class Cmd {
private final static Logger logger = LogManager.getLogger(Cmd.class);
public static final String DEFAULT_TARGET_CONTEXT = "default";
private final String targetContextName;
private final Map<String, CmdArg> cmdArgs;
private final String stepName;
public String getTargetContext() {
return targetContextName;
}
public Cmd forTargetContext(String contextName, String stepName) {
return new Cmd(cmdType, cmdArgs, contextName, stepName);
}
public String getArgValueOrNull(String paramName) {
CmdArg cmdArg = this.cmdArgs.get(paramName);
if (cmdArg==null) {
return null;
}
return cmdArg.getValue();
}
public String getArgValue(String paramName) {
CmdArg cmdArg = this.cmdArgs.get(paramName);
if (cmdArg==null) {
throw new BasicError("Could not get param value for undefined arg '" + paramName + "'");
}
return cmdArg.getValue();
}
private final CmdType cmdType;
// public Cmd(@NotNull CmdType cmdType, Map<String, CmdArg> cmdArgs, String targetContextName, String stepName) {
// this.cmdArgs = cmdArgs;
// this.cmdType = cmdType;
// this.targetContextName = targetContextName;
// }
// public Cmd(String indirect, Map<String,String> args) {
//
// }
//
// public Cmd(String cmdType, CmdArg... args) {
//
// }
//
public Cmd(@NotNull String cmdTypeOrName, Map<String, CmdArg> argmap) {
this.cmdType = CmdType.valueOfAnyCaseOrIndirect(cmdTypeOrName);
this.targetContextName = DEFAULT_TARGET_CONTEXT;
this.stepName = "";
this.cmdArgs = new LinkedHashMap<>();
if (this.cmdType == CmdType.indirect) {
this.cmdArgs.put("_impl", new CmdArg(new CmdParam("_impl", s->s, false),"===",cmdTypeOrName));
}
cmdArgs.putAll(argmap);
// if (!basket.isEmpty()) {
// throw new BasicError("extraneous arguments provided for " + this + ": " + basket);
// }
}
public Cmd(@NotNull CmdType cmdType, Map<String, CmdArg> cmdArgMap) {
this.cmdType = cmdType;
this.stepName="NO-STEP";
this.targetContextName = DEFAULT_TARGET_CONTEXT;
this.cmdArgs = new LinkedHashMap<>();
if (cmdType == CmdType.indirect && !cmdArgMap.containsKey("_impl")) {
throw new RuntimeException("indirect cmd type is invalid without a '_impl' parameter.");
}
cmdArgs.putAll(cmdArgMap);
}
public Cmd(@NotNull CmdType cmdType, Map<String, CmdArg> cmdArgMap, String targetContext, String stepName) {
this.cmdType = cmdType;
this.stepName=stepName;
this.targetContextName = targetContext;
this.cmdArgs = new LinkedHashMap<>();
if (cmdType == CmdType.indirect && !cmdArgMap.containsKey("_impl")) {
throw new RuntimeException("indirect cmd type is invalid without a '_impl' parameter.");
}
this.cmdArgs.putAll(cmdArgMap);
}
public CmdType getCmdType() {
return cmdType;
}
// TODO: Since this replaced an implicit String, the types need to be disambiguated
public Map<String, CmdArg> getArgs() {
return cmdArgs;
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(cmdType.toString());
for (CmdArg value : getArgs().values()) {
sb.append(" ").append(value);
}
return sb.toString();
}
public String asScriptText() {
StringBuilder sb = new StringBuilder();
sb.append(getCmdType().toString());
sb.append("(");
if (this.cmdArgs.size() > getCmdType().getPositionalArgs().length) {
sb.append(toJSONBlock(getArgMap(), false));
} else {
for (String value : getArgMap().values()) {
String trimmed = ((value.startsWith("'") && value.endsWith("'"))
|| (value.startsWith("\"")) && value.endsWith("\"")) ?
value.substring(1, value.length() - 1) : value;
sb.append("'").append(trimmed).append("'").append(",");
}
sb.setLength(sb.length() - 1);
}
sb.append(");");
return sb.toString();
}
// public static Cmd parseArg(LinkedList<String> arglist, PathCanonicalizer fixer) {
//
// String cmdName = arglist.removeFirst();
// CmdType cmdType = CmdType.valueOfAnyCaseOrIndirect(cmdName);
//
// Map<String, String> params = new LinkedHashMap<>();
//
// for (CmdParam<?> cmdParam : cmdType.getPositionalArgs()) {
//
// String nextarg = arglist.peekFirst();
//
// if (nextarg == null) {
// throw new InvalidParameterException(
// "command '" + cmdName + " requires a value for " + cmdParam.name
// + ", but there were no remaining arguments after it.");
// } else if (cmdParam.freeform) {
// logger.debug(() -> "freeform parameter:" + nextarg);
// } else if (nextarg.contains("=")) {
// throw new InvalidParameterException(
// "command '" + cmdName + "' requires a value for " + cmdParam.name +
// ", but a named parameter was found instead: " + nextarg);
// } else if (CmdType.anyMatches(nextarg)) {
// throw new InvalidParameterException(
// "command '" + cmdName + "' requires a value for " + cmdParam.name
// + ", but a reserved word was found instead: " + nextarg);
// }
//
// logger.debug(() -> "cmd name:" + cmdName + ", positional " + cmdParam.name + ": " + nextarg);
// params.put(cmdParam.name, cmdParam.converter.apply(arglist.removeFirst()).toString());
// }
//
// while (arglist.size() > 0 &&
// !CmdType.anyMatches(arglist.peekFirst())
// && arglist.peekFirst().contains("=")) {
// String arg = arglist.removeFirst();
// String[] assigned = arg.split("=", 2);
// String pname = assigned[0];
// String pval = assigned[1];
//
//
// if (pname.equals("yaml") || pname.equals("workload")) {
// pval = fixer.canonicalizePath(pval);
// }
// if (params.containsKey(pname)) {
// throw new InvalidParameterException("parameter '" + pname + "' is already set for '" + cmdType + "' command. For each command," +
// " a named parameter may only be set once. Multiple occurrences are disallowed to avoid errors or ambiguity.");
// }
// params.put(pname, pval);
// }
//
// return new Cmd(cmdType, params);
// }
public static String toJSONBlock(Map<String, String> map, boolean oneline) {
int klen = map.keySet().stream().mapToInt(String::length).max().orElse(1);
StringBuilder sb = new StringBuilder();
List<String> l = new ArrayList<>();
for (Map.Entry<String, String> entries : map.entrySet()) {
String key = entries.getKey();
String value = removeQuotes(entries.getValue());
if (oneline) {
l.add("'" + key + "':'" + value + "'");
} else {
l.add(" '" + key + "': " + " ".repeat(klen - key.length()) + "'" + value + "'");
}
}
return "{" + (oneline ? "" : "\n") + String.join(",\n", l) + (oneline ? "}" : "\n}");
}
private static String removeQuotes(String value) {
if (value.startsWith("'") && value.endsWith("'")) {
return value.substring(1, value.length() - 1);
}
if (value.startsWith("\"") && value.endsWith("\"")) {
return value.substring(1, value.length() - 1);
}
return value;
}
public static String toJSONParams(String varname, Map<String, String> map, boolean oneline) {
return "// params.size==" + map.size() + "\n" + varname + "=" + toJSONBlock(map, oneline);
}
public Map<String, String> getArgMap() {
LinkedHashMap<String, String> argmap = new LinkedHashMap<>();
this.cmdArgs.forEach((k,v) -> {
argmap.put(k,v.getValue());
});
return argmap;
}
// public static List<Cmd> parseCmds(String... arglist) {
// LinkedList<String> ll = new LinkedList<>(Arrays.asList(arglist));
// List<Cmd> cmds = new ArrayList<>();
// while (!ll.isEmpty()) {
// Cmd cmd = parseArg(ll, null);
// cmds.add(cmd);
// }
// return cmds;
// }
}

View File

@@ -0,0 +1,101 @@
/*
* 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.engine.cmdstream;
import io.nosqlbench.engine.api.scenarios.NBCLIScenarioPreprocessor;
import io.nosqlbench.engine.core.lifecycle.session.CmdParser;
import io.nosqlbench.nb.api.errors.BasicError;
import java.util.Set;
/**
* An argument to a command is based on a defined parameter. You can not assign a typed argument value to a
* command without first resolving the specific parameter.
*/
public class CmdArg {
CmdParam param;
private final String operator;
private final String value;
private final static Set<String> OPERATORS = Set.of("=","==","===");
public CmdArg(CmdParam param, String operator, String value) {
if (!OPERATORS.contains(operator)) {
throw new BasicError("You can't use the assignment operator '" + operator + "' with arguments to commands.");
}
this.param = param;
this.operator = operator;
this.value = value;
}
public static CmdArg of(String cmdName, String varname, String equals, String value) {
CmdType type = CmdType.valueOfAnyCaseOrIndirect(cmdName);
if (type==CmdType.indirect) {
return new CmdArg(new CmdParam<String>(varname, s->s, false),equals,value);
} else {
return type.getNamedParam(varname).assign(equals,value);
}
}
public boolean isReassignable() {
return NBCLIScenarioPreprocessor.UNLOCKED.equals(operator);
}
public boolean isFinalSilent() {
return NBCLIScenarioPreprocessor.SILENT_LOCKED.equals(operator);
}
public boolean isFinalVerbose() {
return NBCLIScenarioPreprocessor.VERBOSE_LOCKED.equals(operator);
}
public CmdArg override(String value) {
if (isReassignable()) {
return new CmdArg(this.param, this.operator, value);
} else if (isFinalSilent()) {
return this;
} else if (isFinalVerbose()) {
throw new BasicError("Unable to reassign value for locked param '" + param + operator + value + "'");
} else {
throw new RuntimeException("impossible!");
}
}
public CmdParam getParam() {
return param;
}
public String getValue() {
return value;
}
public String getQuotedValue() {
String quoted=value;
for (char c :quoted.toCharArray()){
if (CmdParser.SYMBOLS.indexOf(c)>=0) {
quoted = "'" + quoted +"'";
break;
}
}
return quoted;
}
@Override
public String toString() {
return this.getParam().getName() + this.operator + getQuotedValue();
}
}

View File

@@ -0,0 +1,57 @@
/*
* 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.engine.cmdstream;
import java.util.function.Function;
/**
* A CmdParam is used to define the valid names of arguments and their types.
* These can be strictly type checked, or in some cases freeform where strings are
* are used contextually.
* @param <T> The base type of the argument when paired with this CmdParam
*/
public class CmdParam<T> {
public final String name;
public final Function<String, T> converter;
public final boolean freeform;
public CmdParam(String name, Function<String, T> converter, boolean freeform) {
this.name = name;
this.converter = converter;
this.freeform = freeform;
}
public static <T> CmdParam<T> of(String name, Function<String, T> converter) {
return new CmdParam<>(name, converter, false);
}
public static CmdParam<String> of(String name) {
return new CmdParam<>(name, s -> s, false);
}
public static CmdParam<String> ofFreeform(String name) {
return new CmdParam<>(name, s -> s, true);
}
public CmdArg assign(String equals, String value) {
return new CmdArg(this,equals, value);
}
public String getName() {
return this.name;
}
}

View File

@@ -0,0 +1,78 @@
/*
* 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.engine.cmdstream;
/**
* Command verbs define the names and possible parameters for valid commands.
* One special type known as 'indirect' encodes verbs which are not directly mapped to specific
* command implementations. This type is resolved based on the available commands in the runtime,
* or an error is thrown.
*/
public enum CmdType {
run(),
start(),
stop(CmdParam.of("activity")),
forceStop(CmdParam.of("activity")),
script(CmdParam.of("path", s -> s)),
java(CmdParam.of("class", s -> s)),
await(CmdParam.of("activity")),
waitMillis(CmdParam.of("ms", Long::parseLong)),
fragment(CmdParam.ofFreeform("fragment")),
context(CmdParam.of("name")),
indirect(CmdParam.of("indirect"));
private final CmdParam<?>[] positional;
CmdType(CmdParam<?>... positional) {
this.positional = positional;
}
public static boolean anyMatches(String s) {
CmdType found = valueOfAnyCaseOrIndirect(s);
return found != null;
}
public String[] getPositionalArgNames() {
String[] names = new String[positional.length];
for (int i = 0; i < names.length; i++) {
names[i] = positional[i].name;
}
return names;
}
public static CmdType valueOfAnyCaseOrIndirect(String cmdname) {
for (CmdType value : values()) {
if (cmdname.equals(value.toString()) || cmdname.equalsIgnoreCase(value.toString())) {
return value;
}
}
return indirect;
}
public CmdParam<?>[] getPositionalArgs() {
return positional;
}
public CmdParam<?> getNamedParam(String varname) {
for (CmdParam<?> cmdParam : positional) {
if (cmdParam.name.equals(varname)) {
return cmdParam;
}
}
return new CmdParam<Object>(varname,s -> s, false);
}
}

View File

@@ -0,0 +1,54 @@
/*
* 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.engine.cmdstream;
import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBInvokableCommand;
import io.nosqlbench.nb.api.components.NBComponent;
import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBCommandInfo;
import io.nosqlbench.nb.annotations.ServiceSelector;
import java.util.List;
import java.util.ServiceLoader;
public class NBJavaCommandLoader {
public static Class<? extends NBInvokableCommand> oneExists(String cmdName) {
ServiceLoader<NBCommandInfo> loader = ServiceLoader.load(NBCommandInfo.class);
ServiceSelector<NBCommandInfo> selector = ServiceSelector.of(cmdName, loader);
List<? extends ServiceLoader.Provider<? extends NBCommandInfo>> providers = selector.getAllProviders();
if (providers.size()>1) {
throw new RuntimeException("looking for an optional command for cmdName '" + cmdName +"' but found " + providers.size());
}
if (!providers.isEmpty()) {
return providers.get(0).get().getType();
} else {
return null;
}
}
public static NBInvokableCommand init(String cmdSelector, NBComponent parent, String cmdAlias, String ctxName) {
NBCommandInfo cmdInfo = getSelector(cmdSelector).getOne();
NBInvokableCommand command = cmdInfo.create(parent, cmdAlias, ctxName);
return command;
}
private static ServiceSelector<NBCommandInfo> getSelector(String cmdName) {
ServiceLoader<NBCommandInfo> loader = ServiceLoader.load(NBCommandInfo.class);
ServiceSelector<NBCommandInfo> selector = ServiceSelector.of(cmdName, loader);
return selector;
}
}

View File

@@ -0,0 +1,35 @@
# API notes; Commands
Commands have a very basic type model which facilitates parsing,
configuring, overriding, and basic type checking.
* Cmd <-- This is a configured command, including:
* CmdType <-- the type model for the command:
* It's verb name, AKA a CmdType enum value.
* It's valid parameters, their names and types, aka CmdArgs.
* CmdArg <-- A named assignment as parameter name = argument value:
* referencing a valid CmdArg from the command
* assignment locks for template overrides and restrictions
* The name of the context in which the command should be run
In short:
* cmd
* CmdType enum type
* String name
* CmdParam[] params
* arguments map <name, CmdArg>,
* [param] reference
* assigned value (the actual argument value to the related parameter)
* mutability (unlocked | silent locked | verbose locked)
* activity context name
A command stream consists of a sequence of commands, which is the basis for
setting the logic for a session. NBSession is responsible for taking a sequence of commands
and mapping them into NBInvokableCommands.
Prior to handing a sequence of commands over to a session, the named scenario preprocessor
can evaluate a command line against a specified named scenario template. NBInvokableCommand
resolution does not occur until the session has the command sequence. Thus, the work done by
the named scenario preprocessor is limited to the exposed command types shown above.

View File

@@ -18,12 +18,15 @@ 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.api.labels.NBLabelsFilter;
import io.nosqlbench.api.labels.NBLabelsValidator;
import io.nosqlbench.nb.api.config.standard.ConfigLoader;
import io.nosqlbench.nb.api.config.standard.NBConfigurable;
import io.nosqlbench.nb.api.config.standard.NBConfiguration;
import io.nosqlbench.nb.api.config.standard.NBMapConfigurable;
import io.nosqlbench.nb.api.labels.NBLabelsFilter;
import io.nosqlbench.nb.api.labels.NBLabelsValidator;
import io.nosqlbench.nb.annotations.Service;
import io.nosqlbench.api.annotations.Annotation;
import io.nosqlbench.api.annotations.Annotator;
import io.nosqlbench.nb.api.annotations.Annotation;
import io.nosqlbench.nb.api.annotations.Annotator;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

View File

@@ -16,11 +16,10 @@
package io.nosqlbench.engine.core.clientload;
import com.codahale.metrics.Gauge;
import io.nosqlbench.api.engine.metrics.instruments.NBMetricGauge;
import io.nosqlbench.api.labels.NBLabels;
import io.nosqlbench.components.NBBaseComponent;
import io.nosqlbench.components.NBComponent;
import io.nosqlbench.nb.api.engine.metrics.instruments.NBMetricGauge;
import io.nosqlbench.nb.api.labels.NBLabels;
import io.nosqlbench.nb.api.components.NBBaseComponent;
import io.nosqlbench.nb.api.components.NBComponent;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.concurrent.Executors;
@@ -35,7 +34,7 @@ public class ClientSystemMetricChecker extends NBBaseComponent {
private List<ClientMetric> clientMetrics;
public ClientSystemMetricChecker(NBComponent parent, NBLabels additionalLabels, int pollIntervalSeconds) {
super(parent,additionalLabels);
super(parent,additionalLabels.and("_type","client-metrics"));
this.pollIntervalSeconds = pollIntervalSeconds;
this.scheduler = Executors.newScheduledThreadPool(1);
this.clientMetrics = new ArrayList<>();

View File

@@ -20,13 +20,13 @@ import com.codahale.metrics.Counting;
import com.codahale.metrics.Gauge;
import com.codahale.metrics.MetricAttribute;
import com.codahale.metrics.MetricFilter;
import io.nosqlbench.api.engine.metrics.instruments.*;
import io.nosqlbench.api.engine.metrics.reporters.ConsoleReporter;
import io.nosqlbench.api.engine.metrics.reporters.Log4JMetricsReporter;
import io.nosqlbench.components.NBCreators;
import io.nosqlbench.components.NBComponent;
import io.nosqlbench.components.NBComponentTraversal;
import io.nosqlbench.components.NBFinders;
import io.nosqlbench.nb.api.engine.metrics.instruments.*;
import io.nosqlbench.nb.api.engine.metrics.reporters.ConsoleReporter;
import io.nosqlbench.nb.api.engine.metrics.reporters.Log4JMetricsReporter;
import io.nosqlbench.nb.api.components.NBCreators;
import io.nosqlbench.nb.api.components.NBComponent;
import io.nosqlbench.nb.api.components.NBComponentTraversal;
import io.nosqlbench.nb.api.components.NBFinders;
import io.nosqlbench.engine.core.metrics.NBMetricsSummary;
import java.io.ByteArrayOutputStream;

View File

@@ -16,7 +16,7 @@
package io.nosqlbench.engine.core.lifecycle;
import io.nosqlbench.api.metadata.Indexed;
import io.nosqlbench.nb.api.metadata.Indexed;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;

View File

@@ -16,7 +16,7 @@
package io.nosqlbench.engine.core.lifecycle.activity;
import io.nosqlbench.engine.core.lifecycle.scenario.context.ScenarioActivitiesController;
import io.nosqlbench.engine.core.lifecycle.scenario.context.ContextActivitiesController;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -24,9 +24,9 @@ public class ActivitiesExceptionHandler implements Thread.UncaughtExceptionHandl
private static final Logger logger = LogManager.getLogger(ActivitiesExceptionHandler.class);
private final ScenarioActivitiesController controller;
private final ContextActivitiesController controller;
public ActivitiesExceptionHandler(ScenarioActivitiesController controller) {
public ActivitiesExceptionHandler(ContextActivitiesController controller) {
this.controller = controller;
logger.debug(() -> "Activities exception handler starting up for executor '" + this.controller + "'");
}

View File

@@ -20,9 +20,9 @@ import io.nosqlbench.engine.api.activityapi.core.RunState;
import io.nosqlbench.engine.api.activityapi.core.progress.ProgressMeterDisplay;
import io.nosqlbench.engine.api.activityapi.core.progress.StateCapable;
import io.nosqlbench.engine.api.metrics.IndicatorMode;
import io.nosqlbench.api.engine.metrics.PeriodicRunnable;
import io.nosqlbench.api.engine.util.Unit;
import io.nosqlbench.engine.core.lifecycle.scenario.context.ScenarioActivitiesController;
import io.nosqlbench.nb.api.engine.metrics.PeriodicRunnable;
import io.nosqlbench.nb.api.engine.util.Unit;
import io.nosqlbench.engine.core.lifecycle.scenario.context.ContextActivitiesController;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -35,14 +35,14 @@ public class ActivitiesProgressIndicator implements Runnable {
private final static Logger logger = LogManager.getLogger("PROGRESS");
private final String indicatorSpec;
private final ScenarioActivitiesController sc;
private final ContextActivitiesController sc;
private PeriodicRunnable<ActivitiesProgressIndicator> runnable;
private IndicatorMode indicatorMode = IndicatorMode.console;
private final Set<String> seen = new HashSet<>();
private long intervalMillis = 1L;
public ActivitiesProgressIndicator(ScenarioActivitiesController sc, String indicatorSpec) {
public ActivitiesProgressIndicator(ContextActivitiesController sc, String indicatorSpec) {
this.sc = sc;
this.indicatorSpec = indicatorSpec;
start();

View File

@@ -16,27 +16,27 @@
package io.nosqlbench.engine.core.lifecycle.activity;
import com.codahale.metrics.Gauge;
import io.nosqlbench.api.engine.metrics.instruments.NBMetricGauge;
import io.nosqlbench.api.labels.NBLabeledElement;
import io.nosqlbench.api.labels.NBLabels;
import io.nosqlbench.components.NBComponentSubScope;
import io.nosqlbench.nb.api.engine.metrics.instruments.NBMetricGauge;
import io.nosqlbench.nb.api.labels.NBLabeledElement;
import io.nosqlbench.nb.api.labels.NBLabels;
import io.nosqlbench.nb.api.components.NBComponentExecutionScope;
import io.nosqlbench.engine.api.activityapi.core.*;
import io.nosqlbench.engine.api.activityimpl.MotorState;
import io.nosqlbench.api.annotations.Annotation;
import io.nosqlbench.api.annotations.Layer;
import io.nosqlbench.api.engine.activityimpl.ActivityDef;
import io.nosqlbench.api.engine.activityimpl.ParameterMap;
import io.nosqlbench.nb.api.annotations.Annotation;
import io.nosqlbench.nb.api.annotations.Layer;
import io.nosqlbench.nb.api.engine.activityimpl.ActivityDef;
import io.nosqlbench.nb.api.engine.activityimpl.ParameterMap;
import io.nosqlbench.engine.api.activityapi.core.progress.ProgressCapable;
import io.nosqlbench.engine.api.activityapi.core.progress.ProgressMeterDisplay;
import io.nosqlbench.engine.api.activityimpl.motor.RunStateImage;
import io.nosqlbench.engine.api.activityimpl.motor.RunStateTally;
import io.nosqlbench.engine.core.annotation.Annotators;
import io.nosqlbench.engine.core.lifecycle.ExecutionResult;
import io.nosqlbench.engine.core.lifecycle.IndexedThreadFactory;
import io.nosqlbench.virtdata.userlibs.apps.valuechecker.IndexedThreadFactory;
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.concurrent.*;
import java.util.stream.Collectors;
@@ -64,7 +64,7 @@ public class ActivityExecutor implements NBLabeledElement, ParameterMap.Listener
private static final Logger logger = LogManager.getLogger(ActivityExecutor.class);
private static final Logger activitylogger = LogManager.getLogger("ACTIVITY");
private final List<Motor<?>> motors = new ArrayList<>();
private final LinkedList<Motor<?>> motors = new LinkedList<>();
private final Activity activity;
private final ActivityDef activityDef;
private final RunStateTally tally;
@@ -242,7 +242,7 @@ public class ActivityExecutor implements NBLabeledElement, ParameterMap.Listener
* @param activityDef
* the activityDef for this activity instance
*/
private void adjustMotorCountToThreadParam(ActivityDef activityDef) {
private void adjustMotorCountToThreadParam(ActivityDef activityDef) { // TODO: Ensure that threads area allowed to complete their current op gracefully
logger.trace(() -> ">-pre-adjust->" + getSlotStatus());
reduceActiveMotorCountDownToThreadParam(activityDef);
@@ -274,6 +274,18 @@ public class ActivityExecutor implements NBLabeledElement, ParameterMap.Listener
if (activityDef.getThreads()==0) {
logger.warn("setting threads to zero is not advised. At least one thread has to be active to keep the activity alive.");
}
// LinkedList<Motor<?>> toremove = new LinkedList<>();
// while (activityDef.getThreads()>motors.size()) {
// Motor<?> motor = motors.removeLast();
// toremove.addFirst(motor);
// }
// for (Motor<?> motor : toremove) {
// motor.requestStop();
// }
// for (Motor<?> motor : toremove) {
// motor.removeState();
// }
//
while (motors.size() > activityDef.getThreads()) {
Motor motor = motors.get(motors.size() - 1);
logger.trace(() -> "Stopping cycle motor thread:" + motor);
@@ -379,7 +391,7 @@ public class ActivityExecutor implements NBLabeledElement, ParameterMap.Listener
@Override
public ExecutionResult call() throws Exception {
try (NBComponentSubScope scope = new NBComponentSubScope(activity)) {
try (NBComponentExecutionScope scope = new NBComponentExecutionScope(activity)) {
shutdownHook = new ActivityExecutorShutdownHook(this);
Runtime.getRuntime().addShutdownHook(shutdownHook);

View File

@@ -16,8 +16,8 @@
package io.nosqlbench.engine.core.lifecycle.activity;
import io.nosqlbench.api.engine.activityimpl.ActivityDef;
import io.nosqlbench.components.NBComponent;
import io.nosqlbench.nb.api.engine.activityimpl.ActivityDef;
import io.nosqlbench.nb.api.components.NBComponent;
import io.nosqlbench.engine.api.activityapi.core.Activity;
import io.nosqlbench.engine.api.activityimpl.uniform.StandardActivityType;
import org.apache.logging.log4j.LogManager;

View File

@@ -18,13 +18,13 @@ package io.nosqlbench.engine.core.lifecycle.activity;
import io.nosqlbench.adapter.diag.DriverAdapterLoader;
import io.nosqlbench.adapters.api.activityimpl.uniform.DriverAdapter;
import io.nosqlbench.components.NBComponent;
import io.nosqlbench.api.content.Content;
import io.nosqlbench.api.content.NBIO;
import io.nosqlbench.api.engine.activityimpl.ActivityDef;
import io.nosqlbench.api.errors.BasicError;
import io.nosqlbench.api.spi.SimpleServiceLoader;
import io.nosqlbench.api.system.NBEnvironment;
import io.nosqlbench.nb.api.components.NBComponent;
import io.nosqlbench.nb.api.nbio.Content;
import io.nosqlbench.nb.api.nbio.NBIO;
import io.nosqlbench.nb.api.engine.activityimpl.ActivityDef;
import io.nosqlbench.nb.api.errors.BasicError;
import io.nosqlbench.nb.api.spi.SimpleServiceLoader;
import io.nosqlbench.nb.api.system.NBEnvironment;
import io.nosqlbench.engine.api.activityapi.core.ActivityType;
import io.nosqlbench.engine.api.activityimpl.uniform.StandardActivityType;
import io.nosqlbench.nb.annotations.Maturity;

View File

@@ -16,7 +16,7 @@
package io.nosqlbench.engine.core.lifecycle.process;
import io.nosqlbench.api.errors.BasicError;
import io.nosqlbench.nb.api.errors.BasicError;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.graalvm.polyglot.PolyglotException;

View File

@@ -15,7 +15,7 @@
*/
package io.nosqlbench.engine.core.lifecycle.process;
import io.nosqlbench.api.Shutdownable;
import io.nosqlbench.nb.api.lifecycle.Shutdownable;
import java.util.LinkedList;

View File

@@ -15,7 +15,7 @@
*/
package io.nosqlbench.engine.core.lifecycle.scenario.context;
import io.nosqlbench.api.engine.activityimpl.ActivityDef;
import io.nosqlbench.nb.api.engine.activityimpl.ActivityDef;
import org.graalvm.polyglot.Value;
import org.graalvm.polyglot.proxy.ProxyObject;
@@ -28,11 +28,11 @@ import java.util.stream.Collectors;
*/
public class ActivityBindings implements Bindings, ProxyObject {
private final ScenarioActivitiesController scenario;
private final ContextActivitiesController scenario;
private final Map<String, Bindings> elementMap = new HashMap<String, Bindings>();
public ActivityBindings(ScenarioActivitiesController scenarioActivitiesController) {
this.scenario = scenarioActivitiesController;
public ActivityBindings(ContextActivitiesController contextActivitiesController) {
this.scenario = contextActivitiesController;
}
@Override

View File

@@ -0,0 +1,200 @@
/*
* 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.engine.core.lifecycle.scenario.context;
import io.nosqlbench.engine.core.annotation.Annotators;
import io.nosqlbench.engine.core.lifecycle.activity.ActivitiesProgressIndicator;
import io.nosqlbench.engine.core.lifecycle.scenario.execution.ContextShutdownHook;
import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBCommandResult;
import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBInvokableCommand;
import io.nosqlbench.nb.api.annotations.Annotation;
import io.nosqlbench.nb.api.annotations.Layer;
import io.nosqlbench.nb.api.labels.NBLabels;
import io.nosqlbench.nb.api.components.NBBaseComponent;
import io.nosqlbench.nb.api.components.NBComponent;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.StringReader;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class NBBufferedCommandContext extends NBBaseComponent implements NBCommandContext {
private final static Logger logger = LogManager.getLogger(NBBufferedCommandContext.class);
private final ContextActivitiesController controller;
private final ActivitiesProgressIndicator activitiesProgressIndicator;
private ContextShutdownHook contextShutdownHook;
private long startedAtMillis;
private Exception error;
private long endedAtMillis;
private final Map<String, Object> vars = new LinkedHashMap<>();
public enum IOType {
connected,
virtual,
traced
}
private final IOType iotype;
private DiagWriter stdoutBuffer;
private DiagWriter stderrBuffer;
private DiagReader stdinBuffer;
public NBBufferedCommandContext(NBComponent parent, String name, IOType ioTypes) {
super(parent, NBLabels.forKV("context",name));
this.iotype = ioTypes;
this.controller = new ContextActivitiesController(this);
switch (iotype) {
case traced:
stdoutBuffer = new DiagWriter(new InterjectingCharArrayWriter(" stdout "), new PrintWriter(System.out));
stderrBuffer = new DiagWriter(new InterjectingCharArrayWriter(" stderr "), new PrintWriter(System.out));
stdinBuffer = new DiagReader(new InputStreamReader(System.in), " stdin ");
break;
case virtual:
stdoutBuffer = new DiagWriter(new InterjectingCharArrayWriter(" stdout "));
stderrBuffer = new DiagWriter(new InterjectingCharArrayWriter(" stderr "));
stdinBuffer = new DiagReader(new StringReader(""), " stdin ");
break;
case connected:
stdoutBuffer = new DiagWriter(new PrintWriter(System.out));
stderrBuffer = new DiagWriter(new PrintWriter(System.out));
stdinBuffer = new DiagReader(new InputStreamReader(System.in));
break;
}
this.contextShutdownHook = new ContextShutdownHook(this);
Runtime.getRuntime().addShutdownHook(this.contextShutdownHook);
Annotators.recordAnnotation(
Annotation.newBuilder()
.element(this)
.now()
.layer(Layer.Scenario)
.build()
);
this.activitiesProgressIndicator = new ActivitiesProgressIndicator(this.controller, "console:10s");
}
@Override
public ContextActivitiesController controller() {
return controller;
}
@Override
public PrintWriter out() {
return stdoutBuffer;
}
@Override
public PrintWriter err() {
return stderrBuffer;
}
@Override
public Reader in() {
return stdinBuffer;
}
public String getIOLog() {
return this.stdoutBuffer.getTimedLog() + this.stderrBuffer.getTimedLog();
}
public NBCommandContext asFixtures() {
return (NBCommandContext) this;
}
public static ContextBuilderFacets.WantsName builder() {
return new NBScenarioContextBuilder();
}
@Override
public NBCommandResult apply(NBInvokableCommand nbCmd, NBCommandParams nbCmdParams) {
NBCommandResult safeCmdResult = nbCmd.invokeSafe(this, nbCmdParams);
error=safeCmdResult.getException();
if (error!=null) {
try {
controller.forceStopActivities(3000);
} catch (final Exception eInner) {
logger.debug(() -> "Found inner exception while forcing stop activities with rethrow=false: " + eInner);
}
// throw new RuntimeException(safeCmdResult.getException());
}
Object object = safeCmdResult.getResultObject();
String stepname = getLabels().valueOfOptional("step").orElse("unknownstep");
if (object instanceof Map<?,?> map) {
map.forEach((k,v) -> {
logger.debug("setting command result for '" + stepname + "." + k + "' to '" + v.toString()+"'");
getContainerVars().put(stepname+"."+k,v.toString());
});
getContainerVars().put(stepname+"._map",map);
} else if (object instanceof List<?> list) {
for (int i = 0; i < list.size(); i++) {
getContainerVars().put(stepname+"."+String.valueOf(i),list.get(i));
}
getContainerVars().put(stepname+"._list",list);
} else if (object instanceof Set<?> set) {
getContainerVars().put(stepname+"._set",set);
} else if (object != null && object.getClass().isArray()) {
getContainerVars().put(stepname + "._array", object);
} else if (object !=null) {
getContainerVars().put(stepname + "._object", object);
// ignore
// } else {
//
// RuntimeException error = new RuntimeException("Unrecognizable type to set context vars with:" + object.getClass().getCanonicalName());
// logger.error(error);
//// throw new RuntimeException("Unrecognizable type to set context vars with:" + object.getClass().getCanonicalName());
} else {
logger.debug("no object was provided to set the context result");
}
return safeCmdResult;
}
@Override
public void doShutdown() {
NBCommandContext.super.doShutdown();
}
@Override
public Map<String, Object> getContainerVars() {
return this.vars;
}
@Override
public void beforeDetach() {
// TODO, shutdown hooks need to be moved to context
Runtime.getRuntime().removeShutdownHook(this.contextShutdownHook);
final var retiringScenarioShutdownHook = this.contextShutdownHook;
this.contextShutdownHook = null;
retiringScenarioShutdownHook.run();
this.logger.debug("removing context shutdown hook");
}
}

View File

@@ -0,0 +1,44 @@
/*
* 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.engine.core.lifecycle.scenario.context;
import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBCommandResult;
import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBInvokableCommand;
import io.nosqlbench.nb.api.components.NBComponent;
import os.CommandResult;
import java.io.PrintWriter;
import java.io.Reader;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Function;
public interface NBCommandContext extends NBComponent, BiFunction<NBInvokableCommand,NBCommandParams, NBCommandResult> {
// ScenarioPhaseParams params();
ContextActivitiesController controller();
PrintWriter out();
PrintWriter err();
Reader in();
public static ContextBuilderFacets.WantsName builder() {
return new NBScenarioContextBuilder();
}
default void doShutdown() {
};
Map<String,Object> getContainerVars();
}

View File

@@ -1,115 +0,0 @@
/*
* 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.engine.core.lifecycle.scenario.context;
import io.nosqlbench.api.config.standard.TestComponent;
import io.nosqlbench.components.NBComponent;
import io.nosqlbench.engine.core.lifecycle.session.NBSession;
import java.io.*;
/**
* <P>An NBSceneFixtures instance represents the runtime fixtures needed to run a specific
* scenario. It is instanced per execution and is not expected to be thread-safe nor
* run more than once. This provides all of the required runtime support and IO boundaries
* needed by a scenario.</P>
*
* <P>The properties on this context define the API used by any scenario logic,
* whether implemented in script or directly. This should allow different
* execution forms to read similarly, easing development and debugging of more advanced
* scenarios.</P>
*
* <P>When using the fixtures within a context, they should be named <em>scene</em>
* which suggests an episodic sequence of events.</P>
*
* <P>Within an execution context, scenario logic is expected to adhere to usage of
* <i>scene.in</i>, <i>scene.out</i>, and <i>scene.error</i> instead of System.out, ...</P>
*/
public class NBDefaultSceneFixtures implements NBSceneFixtures {
/*
These are parameters which are passed into the script, named scenario, etc.
*/
private ScenarioParams params;
/*
* NBSession is the root component type in a NB process, and all CLI options which
* affect global settings are expected to be properties on the session.
*/
private NBComponent session;
/*
* ScenarioActivitiesController is the concurrency handling layer for activities within
* a given scenario. A scenario doesn't complete unless until all activities
* are complete or errored.
*/
private ScenarioActivitiesController controller;
/*
* Extensions provide additional scripting capabilities which are not provided by the
* scripting or other runtimes, or new ways of tapping into extant features.
*/
private PrintWriter out;
private PrintWriter err;
private Reader in;
public NBDefaultSceneFixtures(ScenarioParams params, ScenarioActivitiesController controller, PrintWriter out, PrintWriter err, Reader in) {
this.params = params;
this.controller = controller;
this.out = out;
this.err = err;
this.in = in;
}
// public static NBSceneFixtures ofDefault(String name) {
// return new NBDefaultSceneFixtures(
// new ScenarioParams(),
// new NBSession(
// new TestComponent("scene", name), "scene~"+name
// ),
// new ScenarioActivitiesController(),
// new PrintWriter(System.out),
// new PrintWriter(System.err),
// new InputStreamReader(System.in)
// );
// }
//
@Override
public ScenarioParams params() {
return params;
}
@Override
public ScenarioActivitiesController controller() {
return controller;
}
@Override
public PrintWriter out() {
return out;
}
@Override
public PrintWriter err() {
return err;
}
@Override
public Reader in() {
return in;
}
}

View File

@@ -1,127 +0,0 @@
/*
* 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.engine.core.lifecycle.scenario.context;
import io.nosqlbench.api.config.standard.TestComponent;
import io.nosqlbench.api.filtering.TristateFilter;
import io.nosqlbench.components.NBComponent;
import io.nosqlbench.engine.api.scripting.DiagReader;
import io.nosqlbench.engine.api.scripting.DiagWriter;
import io.nosqlbench.engine.api.scripting.InterjectingCharArrayWriter;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.StringReader;
import java.util.Map;
public class NBSceneBuffer implements NBSceneFixtures {
private final NBSceneFixtures fixtures;
public NBSceneBuffer params(Map<String,String> params) {
return new NBSceneBuffer(
new NBDefaultSceneFixtures(
ScenarioParams.of(params),
fixtures.controller(),
fixtures.out(),
fixtures.err(),
fixtures.in())
,iotype
);
}
public enum IOType {
connected,
virtual,
traced
}
private final IOType iotype;
private DiagWriter stdoutBuffer;
private DiagWriter stderrBuffer;
private DiagReader stdinBuffer;
public NBSceneBuffer(NBSceneFixtures fixtures, IOType ioTypes) {
this.iotype = ioTypes;
this.fixtures = fixtures;
switch (iotype) {
case traced:
stdoutBuffer = new DiagWriter(new InterjectingCharArrayWriter(" stdout "), fixtures.out());
stderrBuffer = new DiagWriter(new InterjectingCharArrayWriter(" stderr "), fixtures.err());
stdinBuffer = new DiagReader(fixtures.in(), " stdin ");
break;
case virtual:
stdoutBuffer = new DiagWriter(new InterjectingCharArrayWriter(" stdout "));
stderrBuffer = new DiagWriter(new InterjectingCharArrayWriter(" stderr "));
stdinBuffer = new DiagReader(new StringReader(""), " stdin ");
break;
case connected:
stdoutBuffer = new DiagWriter(fixtures.out());
stderrBuffer = new DiagWriter(fixtures.err());
stdinBuffer = new DiagReader(fixtures.in());
break;
}
}
public NBSceneBuffer(NBSceneFixtures fixtures) {
this(fixtures, IOType.traced);
}
@Override
public ScenarioParams params() {
return fixtures.params();
}
@Override
public ScenarioActivitiesController controller() {
return fixtures.controller();
}
@Override
public PrintWriter out() {
return stdoutBuffer;
}
@Override
public PrintWriter err() {
return stderrBuffer;
}
@Override
public Reader in() {
return stdinBuffer;
}
public String getIOLog() {
return this.stdoutBuffer.getTimedLog() + this.stderrBuffer.getTimedLog();
}
public NBSceneFixtures asFixtures() {
return (NBSceneFixtures) this;
}
public static SceneBuilderFacets.WantsController builder() {
return new SceneBuilder();
}
public static NBSceneBuffer traced(NBComponent component) {
return builder().tracedIO().build(component);
}
}

View File

@@ -1,71 +0,0 @@
/*
* 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.engine.core.lifecycle.scenario.direct;
import io.nosqlbench.components.NBComponent;
import io.nosqlbench.engine.core.lifecycle.scenario.context.ScenarioActivitiesController;
import io.nosqlbench.engine.core.lifecycle.scenario.context.NBSceneFixtures;
import io.nosqlbench.engine.core.lifecycle.scenario.context.ScenarioParams;
import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBScenario;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.Writer;
public abstract class SCBaseScenario extends NBScenario {
protected Reader stdin;
protected PrintWriter stdout;
protected Writer stderr;
protected ScenarioActivitiesController controller;
protected ScenarioParams params;
public SCBaseScenario(NBComponent parentComponent, String scenarioName) {
super(parentComponent, scenarioName);
}
@Override
protected final void runScenario(NBSceneFixtures shell) {
this.stdin = shell.in();
this.stdout = shell.out();
this.stderr = shell.err();
this.controller = shell.controller();
this.params = shell.params();
try {
invoke();
} catch (Exception e) {
stdout.println(e.toString());
throw e;
}
}
/**
* Subclasses must implement this method, which emulates the scope
* of previous scenario scripts. Within this method, local
* fields will be available directly:
* <UL>
* <LI>component, an {@link NBComponent} - The NB component upon which all metrics or other services are attached.</LI>
* <LI>stdin - a {@link Reader} representing the input buffer which would normally be {@link System#in}
* <LI>stdout, stderr</LI>- a {@link PrintWriter}; This can be buffered virtually, attached to {@link System#out} and {@link System#err} or both for IO tracing.</LI>
* <LI>controller - A dedicated {@link ScenarioActivitiesController} which can be used to define, start, top, and interact with activities.</LI>
* <LI>params - The {@link ScenarioParams} which have been passed to this scenario.</LI>
* <LI><EM>all component services</EM> as this scenario IS a component. This includes all implemented methods in any of the {@link NBComponent} sub-interfaces.</EM>
* </LI>
* </UL>
*/
public abstract void invoke();
}

View File

@@ -0,0 +1,76 @@
/*
* 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.engine.core.lifecycle.scenario.execution;
import io.nosqlbench.nb.api.annotations.Annotation;
import io.nosqlbench.nb.api.annotations.Layer;
import io.nosqlbench.nb.api.labels.NBLabels;
import io.nosqlbench.nb.api.components.NBComponent;
import io.nosqlbench.nb.api.components.NBComponentErrorHandler;
import io.nosqlbench.engine.core.annotation.Annotators;
import io.nosqlbench.engine.core.lifecycle.activity.ActivitiesProgressIndicator;
import io.nosqlbench.engine.core.lifecycle.scenario.context.ContextActivitiesController;
import io.nosqlbench.engine.core.lifecycle.scenario.context.NBBufferedCommandContext;
import io.nosqlbench.engine.core.lifecycle.scenario.context.NBCommandParams;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.PrintWriter;
import java.io.Reader;
public abstract class NBBaseCommand extends NBInvokableCommand {
private final String targetScenario;
protected Logger logger = LogManager.getLogger("COMMAND");
public NBBaseCommand(NBBufferedCommandContext parentComponent, String stepName, String targetScenario) {
super(parentComponent, NBLabels.forKV("step", stepName));
this.targetScenario = targetScenario;
}
public NBBaseCommand(NBBufferedCommandContext parentComponent, String commandLabel) {
this(parentComponent, commandLabel, "_testing_");
}
public String getScenarioName() {
return getLabels().asMap().get("scenario");
}
public String getTargetScenario() {
return this.targetScenario;
}
@Override
public final Object apply(NBBufferedCommandContext sctx, NBCommandParams params) {
return invoke(params, sctx.out(), sctx.err(), sctx.in(), sctx.controller());
}
@Override
public String toString() {
return "SCENARIO (" + this.getClass().getSuperclass().getSimpleName() + ") { scenarioName: " + getScenarioName() + " }";
}
public abstract Object invoke(
NBCommandParams params,
PrintWriter stdout,
PrintWriter stderr,
Reader stdin,
ContextActivitiesController controller
);
}

View File

@@ -0,0 +1,46 @@
/*
* 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.engine.core.lifecycle.scenario.execution;
import io.nosqlbench.engine.core.lifecycle.scenario.context.NBBufferedCommandContext;
import io.nosqlbench.nb.api.components.NBComponent;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* Implement this stub service to identify {@link NBInvokableCommand}s which can be loaded in the runtime.
* Each concrete implementation of NBCommandInfo needs to be annotated with
* <pre>{@code
* @Service(value = NBCommandInfo.class, selector = "<cmdname>")
* }</pre>
*/
public abstract class NBCommandInfo {
private final static Logger logger = LogManager.getLogger(NBCommandInfo.class);
public abstract Class<? extends NBInvokableCommand> getType();
public NBInvokableCommand create(NBComponent parent, String cmdName, String ctxName) {
Constructor<? extends NBInvokableCommand> cmdCtor;
try {
cmdCtor = getType().getConstructor(NBBufferedCommandContext.class, String.class, String.class);
return cmdCtor.newInstance(parent, cmdName, ctxName);
} catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) {
throw new RuntimeException("Unable to instantiate command via ctor(parent,name,ctx): " + e,e);
}
}
}

View File

@@ -0,0 +1,63 @@
/*
* 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.engine.core.lifecycle.scenario.execution;
import io.nosqlbench.engine.core.lifecycle.scenario.context.NBBufferedCommandContext;
import io.nosqlbench.engine.core.lifecycle.scenario.context.NBCommandParams;
import io.nosqlbench.nb.api.components.NBBaseComponent;
import io.nosqlbench.nb.api.components.NBComponent;
import io.nosqlbench.nb.api.labels.NBLabels;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Objects;
import java.util.function.BiFunction;
public abstract class NBInvokableCommand extends NBBaseComponent implements BiFunction<NBBufferedCommandContext, NBCommandParams, Object> {
private static final Logger logger = LogManager.getLogger(NBInvokableCommand.class);
public NBInvokableCommand(NBBufferedCommandContext parentComponent, NBLabels componentSpecificLabelsOnly) {
super(parentComponent, componentSpecificLabelsOnly);
}
@Override
public abstract Object apply(NBBufferedCommandContext nbBufferedCommandContext, NBCommandParams nbCommandParams);
public NBCommandResult invokeSafe(NBBufferedCommandContext context, NBCommandParams params) {
Object resultObject = null;
Exception exception = null;
long startAt = System.currentTimeMillis();
NBCommandResult result = null;
try {
logger.debug("invoking command: " + this);
resultObject=apply(context, params);
logger.debug("cmd produced: " + (resultObject==null ? "NULL" : resultObject.toString()));
} catch (Exception e) {
exception = e;
logger.error("error in command (stack trace follows): " + this.description() + ": " + exception);
exception.printStackTrace(System.out);
} finally {
long endAt = System.currentTimeMillis();
result = new NBCommandResult(context, startAt, endAt, exception);
if (resultObject!=null) {
result.setResultObject(resultObject);
}
}
return result;
}
}

View File

@@ -1,217 +0,0 @@
/*
* 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.engine.core.lifecycle.scenario.execution;
import io.nosqlbench.api.annotations.Annotation;
import io.nosqlbench.api.annotations.Layer;
import io.nosqlbench.api.labels.NBLabels;
import io.nosqlbench.api.metadata.ScenarioMetadata;
import io.nosqlbench.api.metadata.SystemId;
import io.nosqlbench.components.NBComponent;
import io.nosqlbench.components.NBBaseComponent;
import io.nosqlbench.components.NBComponentErrorHandler;
import io.nosqlbench.engine.core.annotation.Annotators;
import io.nosqlbench.engine.core.lifecycle.activity.ActivitiesProgressIndicator;
import io.nosqlbench.engine.core.lifecycle.scenario.context.ScenarioActivitiesController;
import io.nosqlbench.engine.core.lifecycle.scenario.context.NBSceneBuffer;
import io.nosqlbench.engine.core.lifecycle.scenario.context.NBSceneFixtures;
import io.nosqlbench.engine.core.lifecycle.scenario.script.NBScriptedScenario;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.function.Function;
/**
* This is the core logic of every NB scenario.
* <OL>
* <LI>NBScenario creates a generic execution context.</LI>
* <LI>This context is functionally applied to (executed by) a specific implementation.</LI>
* <LI>Activities associated with the scenario are completed or errored.</LI>
* <LI>A result is composited from the data in the component tree.</LI>
* </OL>
*/
public abstract class NBScenario extends NBBaseComponent
implements
Function<NBSceneBuffer, ScenarioResult>,
NBComponentErrorHandler {
protected Logger logger = LogManager.getLogger("SCENARIO");
private long startedAtMillis, endedAtMillis;
private ScenarioMetadata scenarioMetadata;
private ScenarioActivitiesController scenarioActivitiesController;
private Exception error;
private String progressInterval = "console:10s";
private ActivitiesProgressIndicator activitiesProgressIndicator;
public NBScenario(NBComponent parentComponent, String scenarioName) {
super(parentComponent, NBLabels.forKV("scenario",scenarioName));
}
public String getScenarioName() {
return getLabels().asMap().get("scenario");
}
public void forceStopScenario(int i, boolean b) {
scenarioActivitiesController.forceStopScenario(i,b);
}
// public Map<String, String> getParams() {
// return this.params;
// }
public ScenarioActivitiesController getActivitiesController() {
return this.scenarioActivitiesController;
}
public enum State {
Scheduled,
Running,
Errored,
Interrupted,
Finished
}
private ScenarioShutdownHook scenarioShutdownHook;
private State state;
/**
* This should be the only way to get a ScenarioResult for a Scenario.
* <p>
* The lifecycle of a scenario includes the lifecycles of all of the following:
* <OL>
* <LI>The scenario control script, executing within a graaljs context.</LI>
* <LI>The lifecycle of every activity which is started within the scenario.</LI>
* </OL>
* <p>
* All of these run asynchronously within the scenario, however the same thread that calls
* the scenario is the one which executes the control script. A scenario ends when all
* of the following conditions are met:
* <UL>
* <LI>The scenario control script has run to completion, or experienced an exception.</LI>
* <LI>Each activity has run to completion, experienced an exception, or all</LI>
* </UL>
*
* @return
*/
@Override
public final ScenarioResult apply(NBSceneBuffer sctx) {
this.scenarioActivitiesController =sctx.controller();
this.scenarioShutdownHook = new ScenarioShutdownHook(this);
Runtime.getRuntime().addShutdownHook(this.scenarioShutdownHook);
this.state = NBScriptedScenario.State.Running;
this.startedAtMillis = System.currentTimeMillis();
Annotators.recordAnnotation(
Annotation.newBuilder()
.element(this)
.now()
.layer(Layer.Scenario)
.build()
);
if (!"disabled".equals(progressInterval) && progressInterval!=null && !progressInterval.isEmpty())
this.activitiesProgressIndicator = new ActivitiesProgressIndicator(scenarioActivitiesController, this.progressInterval);
ScenarioResult result = null;
try {
runScenario(sctx.asFixtures());
final long awaitCompletionTime = 86400 * 365 * 1000L;
this.logger.debug("Awaiting completion of scenario and activities for {} millis.", awaitCompletionTime);
this.scenarioActivitiesController.awaitCompletion(awaitCompletionTime);
} catch (Exception e) {
try {
scenarioActivitiesController.forceStopScenario(3000, false);
} catch (final Exception eInner) {
this.logger.debug("Found inner exception while forcing stop with rethrow=false: {}", eInner);
throw new RuntimeException(e);
}
this.error = e;
} finally {
this.scenarioActivitiesController.shutdown();
this.endedAtMillis = System.currentTimeMillis();
result = new ScenarioResult(
sctx,
startedAtMillis,
endedAtMillis,
error
);
}
Runtime.getRuntime().removeShutdownHook(this.scenarioShutdownHook);
final var retiringScenarioShutdownHook = this.scenarioShutdownHook;
this.scenarioShutdownHook = null;
retiringScenarioShutdownHook.run();
this.logger.debug("removing scenario shutdown hook");
return result;
}
public void notifyException(final Thread t, final Throwable e) {
error = new RuntimeException("in thread " + t.getName() + ", " + e, e);
}
protected abstract void runScenario(NBSceneFixtures sctx);
public void finish() {
this.logger.debug("finishing scenario");
this.endedAtMillis = System.currentTimeMillis(); //TODO: Make only one endedAtMillis assignment
if (State.Running == this.state) state = State.Finished;
if (null != scenarioShutdownHook) {
// If this method was called while the shutdown hook is defined, then it means
// that the scenario was ended before the hook was uninstalled normally.
state = State.Interrupted;
this.logger.warn("Scenario was interrupted by process exit, shutting down");
} else
this.logger.info(
"Scenario completed successfully, with {} logical activities.",
scenarioActivitiesController.getActivityExecutorMap().size()
);
this.logger.info(() -> "scenario state: " + state);
// We report the scenario state via annotation even for short runs
final Annotation annotation = Annotation.newBuilder()
.element(this)
.interval(startedAtMillis, this.endedAtMillis)
.layer(Layer.Scenario)
.addDetail("event", "stop-scenario")
.build();
Annotators.recordAnnotation(annotation);
}
private synchronized ScenarioMetadata getScenarioMetadata() {
if (null == this.scenarioMetadata) scenarioMetadata = new ScenarioMetadata(
startedAtMillis,
getScenarioName(),
SystemId.getNodeId(),
SystemId.getNodeFingerprint()
);
return this.scenarioMetadata;
}
@Override
public String toString() {
return "SCENARIO (" + this.getClass().getSuperclass().getSimpleName()+") { scenarioName: "+getScenarioName()+" }";
}
}

View File

@@ -16,7 +16,7 @@
package io.nosqlbench.engine.core.lifecycle.scenario.execution;
import io.nosqlbench.components.NBComponent;
import io.nosqlbench.nb.api.components.NBComponent;
import io.nosqlbench.engine.core.lifecycle.ExecutionMetricsResult;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

View File

@@ -1,284 +0,0 @@
/*
* 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.
* 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.engine.core.lifecycle.scenario.execution;
import io.nosqlbench.api.errors.BasicError;
import io.nosqlbench.api.labels.NBLabels;
import io.nosqlbench.components.NBComponent;
import io.nosqlbench.components.NBBaseComponent;
import io.nosqlbench.engine.core.lifecycle.ExecutionMetricsResult;
import io.nosqlbench.engine.core.lifecycle.IndexedThreadFactory;
import io.nosqlbench.engine.core.lifecycle.scenario.context.*;
import io.nosqlbench.engine.core.lifecycle.scenario.script.ScenarioExceptionHandler;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;
public class ScenariosExecutor extends NBBaseComponent {
private final Logger logger = LogManager.getLogger("SCENARIOS");
private final LinkedHashMap<String, SubmittedScenario> submitted = new LinkedHashMap<>();
private final ExecutorService executor;
private final String name;
private RuntimeException stoppingException;
public ScenariosExecutor(NBComponent parent, String name, int threads) {
super(parent, NBLabels.forKV("executor","name"));
executor = new ThreadPoolExecutor(1, threads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(),
new IndexedThreadFactory("scenarios", new ScenarioExceptionHandler(this)));
this.name = name;
}
public synchronized void execute(NBScenario scenario, Map<String,String> params) {
if (submitted.get(scenario.getScenarioName()) != null) {
throw new BasicError("Scenario " + scenario.getScenarioName() + " is already defined. Remove it first to reuse the name.");
}
NBSceneBuffer bufferedContext = getNbSceneBuffer(params);
Future<ScenarioResult> future = executor.submit(
() -> scenario.apply(bufferedContext) // combine basic execution data with trace
);
SubmittedScenario s = new SubmittedScenario(scenario, future);
submitted.put(s.getName(), s);
// TODO at this point, bufferedContext holds all the trace, make it visible in results
}
@NotNull
private NBSceneBuffer getNbSceneBuffer(Map<String, String> params) {
return NBSceneBuffer.builder()
.tracedIO()
.params(params)
.build(parent);
}
@Override
public String toString() {
return super.toString();
}
/**
* Shuts down all running scenarios and awaits all results.
*
* @return the final scenario-result map.
*/
public ScenariosResults awaitAllResults() {
return awaitAllResults(Long.MAX_VALUE / 2, 10000); // half max value, to avoid overflow
}
/**
* Shuts down all running scenarios and awaits all results.
*
* @param timeout how long to wait for the results to complete
* @param updateInterval how frequently to log status while waiting
* @return the final scenario-result map
*/
public ScenariosResults awaitAllResults(long timeout, long updateInterval) {
long waitFrom = System.currentTimeMillis();
if (updateInterval > timeout) {
throw new BasicError("timeout must be equal to or greater than updateInterval");
}
long timeoutAt = System.currentTimeMillis() + timeout;
executor.shutdown();
try {
while (!executor.awaitTermination(timeout, TimeUnit.MILLISECONDS)) {
if (System.currentTimeMillis()>=timeoutAt) {
throw new RuntimeException("executor still running scenarios after awaiting all results for " + timeout
+ "ms. isTerminated:" + executor.isTerminated() + " isShutdown:" + executor.isShutdown());
}
}
} catch (InterruptedException ignored) {
}
// while (!executor.isShutdown() && System.currentTimeMillis() < timeoutAt) {
// long waitedAt = System.currentTimeMillis();
// long updateAt = Math.min(timeoutAt, waitedAt + updateInterval);
// while (!executor.isShutdown() && System.currentTimeMillis() < timeoutAt) {
// while (!executor.isShutdown() && System.currentTimeMillis() < updateAt) {
// try {
// long timeRemaining = updateAt - System.currentTimeMillis();
// if (executor.awaitTermination(timeRemaining, TimeUnit.MILLISECONDS)) {
// logger.info("executor shutdown during await");
// }
// } catch (InterruptedException ignored) {
// }
// }
// logger.trace(() -> "waited " + (System.currentTimeMillis()-waitFrom) + " millis for scenarios");
// updateAt = Math.min(timeoutAt, System.currentTimeMillis() + updateInterval);
// }
//
// logger.debug("scenarios executor shutdown after " + (System.currentTimeMillis() - waitedAt) + "ms.");
// }
//
// if (!executor.isShutdown()) {
// throw new RuntimeException("executor still runningScenarios after awaiting all results for " + timeout
// + "ms. isTerminated:" + executor.isTerminated() + " isShutdown:" + executor.isShutdown());
// }
Map<NBScenario, ScenarioResult> scenarioResultMap = new LinkedHashMap<>();
getAsyncResultStatus()
.entrySet()
.forEach(
es -> scenarioResultMap.put(
es.getKey(),
es.getValue().orElse(null)
)
);
return new ScenariosResults(this, scenarioResultMap);
}
/**
* @return list of scenarios which have been submitted, in order
*/
public List<String> getPendingScenarios() {
return new ArrayList<>(
submitted.values().stream()
.map(SubmittedScenario::getName)
.collect(Collectors.toCollection(ArrayList::new)));
}
/**
* <p>Returns a map of all pending scenario names and optional results.
* All submitted scenarios are included. Those which are still pending
* are returned with an empty option.</p>
*
* <p>Results may be exceptional. If {@link ExecutionMetricsResult#getException()} is present,
* then the result did not complete normally.</p>
*
* @return map of async results, with incomplete results as Optional.empty()
*/
public Map<NBScenario, Optional<ScenarioResult>> getAsyncResultStatus() {
Map<NBScenario, Optional<ScenarioResult>> optResults = new LinkedHashMap<>();
for (SubmittedScenario submittedScenario : submitted.values()) {
Future<ScenarioResult> resultFuture = submittedScenario.getResultFuture();
Optional<ScenarioResult> oResult = Optional.empty();
if (resultFuture.isDone()) {
try {
oResult = Optional.of(resultFuture.get());
} catch (Exception e) {
long now = System.currentTimeMillis();
logger.debug("creating exceptional scenario result from getAsyncResultStatus");
throw new RuntimeException(e);
}
}
optResults.put(submittedScenario.getScenario(), oResult);
}
return optResults;
}
public Optional<NBScenario> getPendingScenario(String scenarioName) {
return Optional.ofNullable(submitted.get(scenarioName)).map(SubmittedScenario::getScenario);
}
/**
* Get the result of a pending or completed scenario. If the scenario has run to
* completion, then the Optional will be present. If the scenario threw an
* exception, or there was an error accessing the future, then the result will
* contain the exception. If the callable for the scenario was cancelled, then the
* result will contain an exception stating such.
* <p>
* If the scenario is still pending, then the optional will be empty.
*
* @param scenarioName the scenario name of interest
* @return an optional result
*/
public Optional<Future<ScenarioResult>> getPendingResult(String scenarioName) {
return Optional.ofNullable(submitted.get(scenarioName)).map(s -> s.resultFuture);
}
public synchronized void stopScenario(String scenarioName) {
this.stopScenario(scenarioName, false);
}
public synchronized void stopScenario(String scenarioName, boolean rethrow) {
logger.debug("#stopScenario(name=" + scenarioName + ", rethrow="+ rethrow+")");
Optional<NBScenario> pendingScenario = getPendingScenario(scenarioName);
if (pendingScenario.isPresent()) {
pendingScenario.get().forceStopScenario(10000, true);
} else {
throw new RuntimeException("Unable to cancel scenario: " + scenarioName + ": not found");
}
}
public synchronized void deleteScenario(String scenarioName) {
stopScenario(scenarioName, false);
Optional<NBScenario> pendingScenario = getPendingScenario(scenarioName);
if (pendingScenario.isPresent()) {
submitted.remove(scenarioName);
logger.info(() -> "cancelled scenario " + scenarioName);
} else {
throw new RuntimeException("Unable to cancel scenario: " + scenarioName + ": not found");
}
}
public String getName() {
return name;
}
public synchronized void shutdownNow() {
if (!executor.isShutdown()) {
executor.shutdownNow();
}
}
private static class SubmittedScenario {
private final NBScenario scenario;
private final Future<ScenarioResult> resultFuture;
SubmittedScenario(NBScenario scenario, Future<ScenarioResult> resultFuture) {
this.scenario = scenario;
this.resultFuture = resultFuture;
}
public NBScenario getScenario() {
return scenario;
}
Future<ScenarioResult> getResultFuture() {
return resultFuture;
}
public String getName() {
return scenario.getScenarioName();
}
}
public synchronized void notifyException(Thread t, Throwable e) {
logger.debug(() -> "Scenario executor uncaught exception: " + e.getMessage());
this.stoppingException = new RuntimeException("Error in scenario thread " + t.getName(), e);
}
public ScenarioResult run(NBScenario scenario, Map<String,String> params) {
return scenario.apply(getNbSceneBuffer(params));
}
}

View File

@@ -1,93 +0,0 @@
/*
* 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.
* 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.engine.core.lifecycle.scenario.execution;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
public class ScenariosResults {
private static final Logger logger = LogManager.getLogger(ScenariosResults.class);
private final String scenariosExecutorName;
private final Map<NBScenario, ScenarioResult> scenarioResultMap = new LinkedHashMap<>();
public ScenariosResults(ScenariosExecutor scenariosExecutor) {
this.scenariosExecutorName = scenariosExecutor.getName();
}
public ScenariosResults(ScenariosExecutor scenariosExecutor, Map<NBScenario, ScenarioResult> map) {
this.scenariosExecutorName = scenariosExecutor.getName();
scenarioResultMap.putAll(map);
}
public String getExecutionSummary() {
String sb = "executions: " + scenarioResultMap.size() + " scenarios, " +
scenarioResultMap.values().stream().filter(r -> r.getException()==null).count() + " normal, " +
scenarioResultMap.values().stream().filter(r -> r.getException()!=null).count() + " errored";
return sb;
}
public ScenarioResult getOne() {
if (this.scenarioResultMap.size() != 1) {
throw new RuntimeException("getOne found " + this.scenarioResultMap.size() + " results instead of 1.");
}
return scenarioResultMap.values().stream().findFirst().orElseThrow(
() -> new RuntimeException("Missing result."));
}
public void reportToLog() {
for (Map.Entry<NBScenario, ScenarioResult> entry : this.scenarioResultMap.entrySet()) {
NBScenario scenario = entry.getKey();
ScenarioResult oresult = entry.getValue();
logger.info(() -> "results for scenario: " + scenario);
// if (oresult != null) {
// oresult.reportElapsedMillisToLog();
// } else {
// logger.error(scenario.getScenarioName() + ": incomplete (missing result)");
// }
}
}
public boolean hasError() {
return this.scenarioResultMap.values().stream()
.anyMatch(r -> r.getException()!=null);
}
public Optional<Exception> getAnyError() {
return this.scenarioResultMap.values().stream()
.map(ScenarioResult::getException).filter(Objects::nonNull).findFirst();
}
public int getSize() {
return this.scenarioResultMap.size();
}
@Override
public String toString() {
return scenarioResultMap.values().stream().map(Object::toString).collect(Collectors.joining("\n"));
}
}

View File

@@ -16,33 +16,37 @@
package io.nosqlbench.engine.core.lifecycle.scenario.script;
import io.nosqlbench.engine.core.lifecycle.scenario.context.NBSceneBuffer;
import io.nosqlbench.engine.core.lifecycle.scenario.context.NBSceneFixtures;
import io.nosqlbench.engine.core.lifecycle.scenario.context.NBCommandContext;
import javax.script.SimpleScriptContext;
import java.io.Reader;
import java.io.Writer;
public class BufferedScriptContext extends SimpleScriptContext {
private final NBSceneFixtures fixtures;
public BufferedScriptContext(NBSceneFixtures fixtures) {
this.fixtures = fixtures;
Reader reader;
Writer writer;
Writer errorWriter;
public BufferedScriptContext(Writer writer, Writer errorWriter,Reader reader) {
this.writer = writer;
this.errorWriter = errorWriter;
this.reader = reader;
}
@Override
public Reader getReader() { // stdin
return fixtures.in();
return this.reader;
}
@Override
public Writer getWriter() { // stdout
return fixtures.out();
return this.writer;
}
@Override
public Writer getErrorWriter() { // stderr
return fixtures.err();
return this.errorWriter;
}
}

View File

@@ -0,0 +1,222 @@
/*
* 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.
* 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.engine.core.lifecycle.scenario.script;
import com.oracle.truffle.js.scriptengine.GraalJSScriptEngine;
import io.nosqlbench.engine.cmdstream.BasicScriptBuffer;
import io.nosqlbench.engine.cmdstream.Cmd;
import io.nosqlbench.engine.core.lifecycle.scenario.context.NBBufferedCommandContext;
import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBBaseCommand;
import io.nosqlbench.engine.core.lifecycle.ExecutionMetricsResult;
import io.nosqlbench.engine.core.lifecycle.activity.ActivitiesProgressIndicator;
import io.nosqlbench.engine.core.lifecycle.scenario.context.NBCommandParams;
import io.nosqlbench.engine.core.lifecycle.scenario.context.ContextActivitiesController;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Engine.Builder;
import org.graalvm.polyglot.EnvironmentAccess;
import org.graalvm.polyglot.HostAccess;
import org.graalvm.polyglot.PolyglotAccess;
import org.graalvm.polyglot.io.IOAccess;
import javax.script.*;
import java.io.PrintWriter;
import java.io.Reader;
import java.util.*;
public class NBScriptedCommand extends NBBaseCommand {
private final Invocation invocation = Invocation.EXECUTE_SCRIPT;
private final BasicScriptBuffer buffer;
private Exception error;
private ExecutionMetricsResult result;
private BufferedScriptContext context;
private GraalJSScriptEngine _engine;
public Optional<ExecutionMetricsResult> getResultIfComplete() {
return Optional.ofNullable(result);
}
public NBScriptedCommand add(BasicScriptBuffer otherBuf) {
this.buffer.add(otherBuf);
return this;
}
public enum Invocation {
RENDER_SCRIPT,
EXECUTE_SCRIPT
}
// private final List<String> scripts = new ArrayList<>();
private ActivitiesProgressIndicator activitiesProgressIndicator;
private String progressInterval = "console:1m";
// private ScenarioScriptShell scriptEnv;
private final String phaseName;
private NBCommandParams scenarioScenarioParams;
private final Engine engine = Engine.Graalvm;
private long startedAtMillis = -1L;
private long endedAtMillis = -1L;
public enum Engine {
Graalvm
}
public NBScriptedCommand(
NBBufferedCommandContext parentComponent,
String phaseName,
String targetScenario
) {
super(parentComponent, phaseName, targetScenario);
this.phaseName = phaseName;
this.progressInterval = progressInterval;
this.buffer = new BasicScriptBuffer();
}
public static NBScriptedCommand ofScripted(String name, Map<String, String> params, NBBufferedCommandContext parent, Invocation invocation) {
return new NBScriptedCommand(parent, name, "default");
}
public NBScriptedCommand add(Cmd... cmds) {
this.buffer.add(cmds);
return this;
}
// public NBScriptedCommand addScriptText(final String scriptText) {
// this.scripts.add(scriptText);
// return this;
// }
//
// public NBScriptedCommand addScriptFiles(final String... args) {
// for (final String scriptFile : args) {
// final Path scriptPath = Paths.get(scriptFile);
// byte[] bytes = new byte[0];
// try {
// bytes = Files.readAllBytes(scriptPath);
// } catch (final IOException e) {
// e.printStackTrace();
// }
// final ByteBuffer bb = ByteBuffer.wrap(bytes);
// final Charset utf8 = StandardCharsets.UTF_8;
// final String scriptData = utf8.decode(bb).toString();
// this.addScriptText(scriptData);
// }
// return this;
// }
private BufferedScriptContext initializeScriptContext(PrintWriter stdout, PrintWriter stderr, Reader stdin, ContextActivitiesController controller) {
BufferedScriptContext ctx = new BufferedScriptContext(stdout, stderr, stdin);
ctx.getBindings(ScriptContext.ENGINE_SCOPE).put("this", this);
ctx.getBindings(ScriptContext.ENGINE_SCOPE).put("context", context);
ctx.getBindings(ScriptContext.ENGINE_SCOPE).put("controller", controller);
ctx.getBindings(ScriptContext.ENGINE_SCOPE).put("stdout", stdout);
ctx.getBindings(ScriptContext.ENGINE_SCOPE).put("stderr", stderr);
ctx.getBindings(ScriptContext.ENGINE_SCOPE).put("stdin", stdin);
return ctx;
}
private GraalJSScriptEngine initializeScriptingEngine() {
if (_engine == null) {
final Context.Builder contextSettings = Context.newBuilder("js")
.allowHostAccess(HostAccess.ALL)
.allowNativeAccess(true)
.allowCreateThread(true)
.allowIO(IOAccess.ALL)
.allowHostClassLookup(s -> true)
.allowHostClassLoading(true)
.allowCreateProcess(true)
.allowAllAccess(true)
.allowEnvironmentAccess(EnvironmentAccess.INHERIT)
.allowPolyglotAccess(PolyglotAccess.ALL)
.option("js.ecmascript-version", "2022")
.option("js.nashorn-compat", "true");
final Builder engineBuilder = org.graalvm.polyglot.Engine.newBuilder();
engineBuilder.option("engine.WarnInterpreterOnly", "false");
final org.graalvm.polyglot.Engine polyglotEngine = engineBuilder.build();
this._engine = GraalJSScriptEngine.create(polyglotEngine, contextSettings);
}
return this._engine;
}
@Override
public Object invoke(NBCommandParams params, PrintWriter stdout, PrintWriter stderr, Reader stdin, ContextActivitiesController controller) {
try {
this.logger.debug("Initializing scripting engine for {}.", phaseName);
GraalJSScriptEngine engine = this.initializeScriptingEngine();
this.context = this.initializeScriptContext(stdout, stderr, stdin, controller);
this.logger.debug("Running control script for {}.", phaseName);
engine.setContext(context);
engine.getContext().getBindings(ScriptContext.ENGINE_SCOPE).put("params", params);
Object resultObject = null;
// for (final String script : this.scripts) {
// if ((engine instanceof Compilable compilableEngine)) {
// this.logger.debug("Using direct script compilation");
// final CompiledScript compiled = compilableEngine.compile(script);
// this.logger.debug("-> invoking main scenario script (compiled)");
// resultObject = compiled.eval(this.context);
// this.logger.debug("<- scenario script completed (compiled)");
// } else {
this.logger.debug("-> invoking main scenario script (interpreted)");
// engine.getContext().getBindings(ScriptContext.ENGINE_SCOPE).put("this", this);
// engine.getContext().getBindings(ScriptContext.ENGINE_SCOPE).put("params", params);
// engine.getContext().getBindings(ScriptContext.ENGINE_SCOPE).put("context", context);
// engine.getContext().getBindings(ScriptContext.ENGINE_SCOPE).put("controller", controller);
// engine.getContext().getBindings(ScriptContext.ENGINE_SCOPE).put("stdout", stdout);
// engine.getContext().getBindings(ScriptContext.ENGINE_SCOPE).put("stderr", stderr);
// engine.getContext().getBindings(ScriptContext.ENGINE_SCOPE).put("stdin", stdin);
resultObject = engine.eval(buffer.getParsedScript());
this.logger.debug("<- scenario control script completed (interpreted)");
// }
return resultObject;
// }
} catch (ScriptException e) {
throw new RuntimeException(e);
} finally {
this.endedAtMillis = System.currentTimeMillis();
// this.logger.debug("{} scenario run", null == this.error ? "NORMAL" : "ERRORED");
}
// return null;
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if ((null == o) || (this.getClass() != o.getClass())) {
return false;
}
final NBScriptedCommand scenario = (NBScriptedCommand) o;
return Objects.equals(this.phaseName, scenario.phaseName);
}
@Override
public int hashCode() {
return (null != this.phaseName) ? phaseName.hashCode() : 0;
}
public String toString() {
return description();
}
}

View File

@@ -1,211 +0,0 @@
/*
* 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.
* 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.engine.core.lifecycle.scenario.script;
import com.oracle.truffle.js.scriptengine.GraalJSScriptEngine;
import io.nosqlbench.components.NBComponent;
import io.nosqlbench.engine.core.lifecycle.ExecutionMetricsResult;
import io.nosqlbench.engine.core.lifecycle.activity.ActivitiesProgressIndicator;
import io.nosqlbench.engine.core.lifecycle.scenario.context.NBSceneFixtures;
import io.nosqlbench.engine.core.lifecycle.scenario.context.ScenarioParams;
import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBScenario;
import io.nosqlbench.engine.core.lifecycle.scenario.script.bindings.PolyglotScenarioController;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Engine.Builder;
import org.graalvm.polyglot.EnvironmentAccess;
import org.graalvm.polyglot.HostAccess;
import org.graalvm.polyglot.PolyglotAccess;
import javax.script.*;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;
public class NBScriptedScenario extends NBScenario {
private final Invocation invocation = Invocation.EXECUTE_SCRIPT;
private Exception error;
private ExecutionMetricsResult result;
private BufferedScriptContext context;
public Optional<ExecutionMetricsResult> getResultIfComplete() {
return Optional.ofNullable(result);
}
public enum Invocation {
RENDER_SCRIPT,
EXECUTE_SCRIPT
}
private final List<String> scripts = new ArrayList<>();
private ScriptEngine scriptEngine;
private ActivitiesProgressIndicator activitiesProgressIndicator;
private String progressInterval = "console:1m";
// private ScenarioScriptShell scriptEnv;
private final String scenarioName;
private ScenarioParams scenarioScenarioParams;
private final Engine engine = Engine.Graalvm;
private long startedAtMillis = -1L;
private long endedAtMillis = -1L;
public enum Engine {
Graalvm
}
public NBScriptedScenario(
final String scenarioName,
NBComponent parentComponent
) {
super(parentComponent, scenarioName);
this.scenarioName = scenarioName;
this.progressInterval = progressInterval;
}
public static NBScriptedScenario ofScripted(String name, Map<String, String> params, NBComponent parent, Invocation invocation) {
return new NBScriptedScenario(name, parent);
}
;
public NBScriptedScenario addScriptText(final String scriptText) {
this.scripts.add(scriptText);
return this;
}
public NBScriptedScenario addScriptFiles(final String... args) {
for (final String scriptFile : args) {
final Path scriptPath = Paths.get(scriptFile);
byte[] bytes = new byte[0];
try {
bytes = Files.readAllBytes(scriptPath);
} catch (final IOException e) {
e.printStackTrace();
}
final ByteBuffer bb = ByteBuffer.wrap(bytes);
final Charset utf8 = StandardCharsets.UTF_8;
final String scriptData = utf8.decode(bb).toString();
this.addScriptText(scriptData);
}
return this;
}
private BufferedScriptContext initializeScriptContext(NBSceneFixtures fixtures) {
BufferedScriptContext ctx = new BufferedScriptContext(fixtures);
// this.scriptEngine.setContext(ctx);
ctx.getBindings(ScriptContext.ENGINE_SCOPE).put("scenario", new PolyglotScenarioController(fixtures.controller()));
return ctx;
}
private void initializeScriptingEngine() {
final Context.Builder contextSettings = Context.newBuilder("js")
.allowHostAccess(HostAccess.ALL)
.allowNativeAccess(true)
.allowCreateThread(true)
.allowIO(true)
.allowHostClassLookup(s -> true)
.allowHostClassLoading(true)
.allowCreateProcess(true)
.allowAllAccess(true)
.allowEnvironmentAccess(EnvironmentAccess.INHERIT)
.allowPolyglotAccess(PolyglotAccess.ALL)
.option("js.ecmascript-version", "2022")
.option("js.nashorn-compat", "true");
final Builder engineBuilder = org.graalvm.polyglot.Engine.newBuilder();
engineBuilder.option("engine.WarnInterpreterOnly", "false");
final org.graalvm.polyglot.Engine polyglotEngine = engineBuilder.build();
scriptEngine = GraalJSScriptEngine.create(polyglotEngine, contextSettings);
}
protected final void runScenario(NBSceneFixtures shell) {
try {
this.logger.debug("Initializing scripting engine for {}.", scenarioName);
this.initializeScriptingEngine();
this.context = this.initializeScriptContext(shell);
this.logger.debug("Running control script for {}.", scenarioName);
this.executeScenarioScripts();
} catch (ScriptException e) {
throw new RuntimeException(e);
} finally {
this.endedAtMillis = System.currentTimeMillis();
// this.logger.debug("{} scenario run", null == this.error ? "NORMAL" : "ERRORED");
}
// String iolog = error != null ? error.toString() : this.scriptEnv.getTimedLog();
// result = new ExecutionMetricsResult(startedAtMillis, endedAtMillis, iolog, this.error);
// this.result.reportMetricsSummaryToLog();
}
private void executeScenarioScripts() throws ScriptException {
for (final String script : this.scripts) {
if ((scriptEngine instanceof Compilable compilableEngine)) {
this.logger.debug("Using direct script compilation");
final CompiledScript compiled = compilableEngine.compile(script);
this.logger.debug("-> invoking main scenario script (compiled)");
compiled.eval(this.context);
this.logger.debug("<- scenario script completed (compiled)");
} else {
this.logger.debug("-> invoking main scenario script (interpreted)");
this.scriptEngine.eval(script);
this.logger.debug("<- scenario control script completed (interpreted)");
}
}
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if ((null == o) || (this.getClass() != o.getClass())) {
return false;
}
final NBScriptedScenario scenario = (NBScriptedScenario) o;
return Objects.equals(this.scenarioName, scenario.scenarioName);
}
@Override
public int hashCode() {
return (null != this.scenarioName) ? scenarioName.hashCode() : 0;
}
public String toString() {
return "name:'" + scenarioName + '\'';
}
// public void addScenarioScriptParams(final ScriptParams scenarioScriptParams) {
// this.scenarioScriptParams = scenarioScriptParams;
// }
// public void addScenarioScriptParams(final Map<String, String> scriptParams) {
// this.addScenarioScriptParams(new ScriptParams() {{
// this.putAll(scriptParams);
// }});
// }
}

View File

@@ -1,32 +0,0 @@
/*
* Copyright (c) 2022 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.engine.core.lifecycle.scenario.script;
import io.nosqlbench.engine.core.lifecycle.scenario.execution.ScenariosExecutor;
public class ScenarioExceptionHandler implements Thread.UncaughtExceptionHandler {
private final ScenariosExecutor scenariosExecutor;
public ScenarioExceptionHandler(ScenariosExecutor scenariosExecutor) {
this.scenariosExecutor = scenariosExecutor;
}
@Override
public void uncaughtException(Thread t, Throwable e) {
scenariosExecutor.notifyException(t, e);
}
}

View File

@@ -15,12 +15,12 @@
*/
package io.nosqlbench.engine.core.lifecycle.scenario.script;
import io.nosqlbench.engine.api.scripting.ScriptEnvBuffer;
import io.nosqlbench.engine.core.lifecycle.scenario.context.NBSceneFixtures;
import io.nosqlbench.engine.core.lifecycle.scenario.context.ScriptEnvBuffer;
import io.nosqlbench.engine.core.lifecycle.scenario.context.NBCommandContext;
public class ScenarioScriptShell extends ScriptEnvBuffer {
public ScenarioScriptShell(NBSceneFixtures fixtures) {
public ScenarioScriptShell(NBCommandContext fixtures) {
}
@Override

View File

@@ -16,8 +16,8 @@
package io.nosqlbench.engine.core.lifecycle.scenario.script.bindings;
import io.nosqlbench.api.engine.activityimpl.ActivityDef;
import io.nosqlbench.engine.core.lifecycle.scenario.context.ScenarioActivitiesController;
import io.nosqlbench.nb.api.engine.activityimpl.ActivityDef;
import io.nosqlbench.engine.core.lifecycle.scenario.context.ContextActivitiesController;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.graalvm.polyglot.Value;
@@ -29,9 +29,9 @@ public class PolyglotScenarioController {
private static final Logger logger = LogManager.getLogger("SCENARIO/POLYGLOT");
private final ScenarioActivitiesController controller;
private final ContextActivitiesController controller;
public PolyglotScenarioController(ScenarioActivitiesController inner) {
public PolyglotScenarioController(ContextActivitiesController inner) {
this.controller = inner;
}

View File

@@ -16,7 +16,7 @@
package io.nosqlbench.engine.core.lifecycle.session;
import io.nosqlbench.engine.cli.Cmd;
import io.nosqlbench.engine.cmdstream.Cmd;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -59,8 +59,8 @@ public class CmdParamsBuffer {
* @param cmd The command containing the new params to merge in
*/
private void combineGlobalParams(Map<String, String> scriptParams, Cmd cmd) {
for (String newkey : cmd.getParams().keySet()) {
String newvalue = cmd.getParams().get(newkey);
for (String newkey : cmd.getArgs().keySet()) {
String newvalue = cmd.getArgs().get(newkey).getValue();
if (scriptParams.containsKey(newkey)) {
logger.warn("command '" + cmd.getCmdType() + "' overwrote param '" + newkey + " as " + newvalue);

View File

@@ -0,0 +1,273 @@
/*
* 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.engine.core.lifecycle.session;
import io.nosqlbench.engine.cmdstream.Cmd;
import io.nosqlbench.engine.cmdstream.CmdArg;
import io.nosqlbench.nb.api.errors.BasicError;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* <P>Take zero or more strings containing combined argv and return
* a single {@link Cmd} list containing zero or more commands.</P>
* <p>
* {@see <a href="https://pubs.opengroup.org/onlinepubs/9699919799/functions/wordexp.html">POSIX wordexp</a>},
* of which this is a shallow substitute.</P>
*/
public class CmdParser {
public final static String SYMBOLS = "`~!@#$%^&*(){}[]|+?!";
public static List<Cmd> parse(String... strings) {
List<Cmd> cmds = new LinkedList<>();
for (String string : strings) {
cmds.addAll(parseCmdString(string));
}
return cmds;
}
private record parameter(String name, String op, String value) {}
private record command(String name){}
private final static Pattern combinedPattern =
Pattern.compile("(?<varname>[a-zA-Z_][a-zA-Z0-9_.-]+)(?<operator>=+)(?<value>.+)|(?<command>[a-zA-Z_][a-zA-Z0-9_.]+)",Pattern.DOTALL);
private final static Pattern commandName =Pattern.compile("^$");
public static LinkedList<Cmd> parseArgvCommands(LinkedList<String> args) {
LinkedList<Record> cmdstructs = new LinkedList<>();
LinkedList<Cmd> cmds = new LinkedList<>();
while (args.size()>0) {
String arg=args.peekFirst();
Matcher matcher = combinedPattern.matcher(arg);
if (matcher.matches()) {
args.removeFirst();
String command = matcher.group("command");
String varname = matcher.group("varname");
String operator = matcher.group("operator");
String value = matcher.group("value");
cmdstructs.add(command!=null ? new command(command) : new parameter(varname,operator,value));
} else {
break;
// throw new BasicError("Unable to parse arg as a command or an assignment: '"+arg+"'");
}
}
while (!cmdstructs.isEmpty()) {
if (cmdstructs.peekFirst() instanceof command cmd) {
cmdstructs.removeFirst();
Map<String,CmdArg> params = new LinkedHashMap<>();
while (cmdstructs.peekFirst() instanceof parameter param) {
cmdstructs.removeFirst();
params.put(param.name(),CmdArg.of(cmd.name(),param.name(),param.op(),param.value()));
}
cmds.add(new Cmd(cmd.name(),params));
} else {
throw new BasicError("first word in argv is not a command: '" + cmdstructs.peekFirst() + "'");
}
}
return cmds;
}
public static enum PS {
barename_start, barename, equals, barevalue, dquote, squote, end
}
private static List<? extends Cmd> parseCmdString(String line) {
List<Cmd> cmds = new LinkedList<>();
LinkedHashMap<String, CmdArg> args = new LinkedHashMap<>();
String cmdName = null;
String varname = null, equals = null, value = null;
StringBuilder buf = new StringBuilder(1024);
PS state = PS.barename_start;
int pos = -1;
while (state != PS.end) {
char at = 0;
CharType type;
if (++pos >= line.length()) {
type = CharType.EOI;
} else {
at = line.charAt(pos);
type = CharType.of(at);
}
if (type == CharType.unknown) throw new BasicError("Unknown character class for '" + at + "'");
state = switch (state) {
case barename_start -> switch (type) {
case space -> PS.barename_start;
case alpha, underscore -> {
buf.setLength(0);
buf.append(at);
yield PS.barename;
}
case EOI -> {
if (cmdName != null) {
cmds.add(new Cmd(cmdName, args));
}
yield PS.end;
}
default -> PS_error(at, pos, state, type);
};
case barename -> switch (type) {
case alpha, numeric, underscore, dot -> {
buf.append(at);
yield PS.barename;
}
case space -> {
if (cmdName != null) cmds.add(new Cmd(cmdName, args));
args = new LinkedHashMap<>();
cmdName = buf.toString().trim();
buf.setLength(0);
yield PS.barename_start;
}
case EOI -> {
cmds.add(new Cmd(buf.toString().trim(), args));
yield PS.end;
}
case equals -> {
if (cmdName == null)
PS_error(at, pos, state, type, "parameter found while no command has been specified");
varname = buf.toString();
buf.setLength(0);
buf.append(at);
yield PS.equals;
}
default -> PS_error(at, pos, state, type);
};
case barevalue -> switch (type) {
case space -> {
value = buf.toString();
args.put(varname, CmdArg.of(cmdName, varname, equals, value));
varname = null;
equals = null;
buf.setLength(0);
yield PS.barename_start;
}
case EOI -> {
value = buf.toString();
args.put(varname, CmdArg.of(cmdName, varname, equals, value));
cmds.add(new Cmd(cmdName, args));
varname = null;
equals = null;
buf.setLength(0);
yield PS.end;
}
default -> {
buf.append(at);
yield PS.barevalue;
}
};
case dquote -> switch (type) {
case dquote -> {
value = buf.toString();
args.put(varname, CmdArg.of(cmdName, varname, equals, value));
varname = null;
equals = null;
buf.setLength(0);
yield PS.barename_start;
}
case EOI ->
PS_error(at, pos, state, type, "reached end of input while reading double-quoted value");
default -> {
buf.append(at);
yield PS.dquote;
}
};
case squote -> switch (type) {
case squote -> {
value = buf.toString();
args.put(varname, CmdArg.of(cmdName, varname, equals, value));
varname = null;
equals = null;
buf.setLength(0);
yield PS.barename_start;
}
case EOI ->
PS_error(at, pos, state, type, "reached end of input while reading single-quoted value");
default -> {
buf.append(at);
yield PS.squote;
}
};
case equals -> switch (type) {
case equals -> {
buf.append(at);
yield PS.equals;
}
case dquote -> {
equals = buf.toString();
buf.setLength(0);
yield PS.dquote;
}
case squote -> {
equals = buf.toString();
buf.setLength(0);
yield PS.squote;
}
case alpha, numeric, underscore, symbol -> {
equals = buf.toString();
buf.setLength(0);
buf.append(at);
yield PS.barevalue;
}
default -> PS_error(at, pos, state, type);
};
case end ->
throw new RuntimeException("Invalid fallthrough to end state. This should have been skipped");
};
}
return cmds;
}
private static PS PS_error(char at, int pos, PS state, CharType type, String... msg) {
throw new BasicError("invalid char '" + at + "' at position " + pos + " in parser state '" + state + "'"
+ (type != null ? " for type '" + type.name() + "'" : "")
+ ((msg.length > 0) ? " " + String.join(", ", Arrays.asList(msg)) : ""));
}
private static enum CharType {
alpha,
numeric,
squote,
dquote,
equals,
underscore,
space,
dot,
newline,
symbol,
EOI,
unknown;
static CharType of(char c) {
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) return alpha;
if ((c >= '0' && c <= '9')) return numeric;
if (c == '_') return underscore;
if (c == '.') return dot;
if (c == '=') return equals;
if (c == '\'') return squote;
if (c == '"') return dquote;
if (c == ' ' || c == '\t') return space;
if (c == '\n' || c == '\r') return newline;
if (SYMBOLS.indexOf(c) >= 0) return symbol;
return unknown;
}
}
}

View File

@@ -0,0 +1,59 @@
/*
* 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.engine.core.lifecycle.session;
import io.nosqlbench.engine.cmdstream.Cmd;
import io.nosqlbench.engine.cmdstream.CmdArg;
import io.nosqlbench.engine.cmdstream.CmdParam;
import io.nosqlbench.engine.cmdstream.CmdType;
import io.nosqlbench.engine.core.lifecycle.scenario.context.NBBufferedCommandContext;
import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBInvokableCommand;
import io.nosqlbench.engine.core.lifecycle.scenario.script.NBScriptedCommand;
import io.nosqlbench.nb.annotations.Service;
import io.nosqlbench.nb.api.nbio.Content;
import io.nosqlbench.nb.api.nbio.NBIO;
import java.nio.file.Path;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@Service(value = NBInvokableResolver.class, selector = "autojs")
public class NBAutoScriptResolver implements NBInvokableResolver {
@Override
public NBInvokableCommand resolve(Cmd cmd, NBBufferedCommandContext parent, String phaseName) {
Optional<Content<?>> scriptfile = NBIO.local()
.searchPrefixes("scripts/auto")
.pathname(cmd.getArgValue("_impl"))
.extensionSet("js")
.first();
if (scriptfile.isPresent()) {
Path pathOf = scriptfile.get().asPath();
Map<String, CmdArg> newArgs = new LinkedHashMap<>(cmd.getArgs());
newArgs.put("path",new CmdArg(new CmdParam("name",s->s,false),"=",pathOf.toString()));
Cmd reformattedCmd = new Cmd("script", newArgs);
return new NBScriptedCommand(parent, phaseName, cmd.getTargetContext()).add(reformattedCmd);
} else {
return null;
}
}
}

View File

@@ -0,0 +1,116 @@
/*
* 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.engine.core.lifecycle.session;
import io.nosqlbench.engine.cmdstream.*;
import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBInvokableCommand;
import io.nosqlbench.engine.core.lifecycle.scenario.context.NBBufferedCommandContext;
import io.nosqlbench.engine.core.lifecycle.scenario.context.NBCommandParams;
import io.nosqlbench.nb.api.errors.BasicError;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.*;
import java.util.function.Function;
public class NBCommandAssembly {
private final static Logger logger = LogManager.getLogger(NBCommandAssembly.class);
public static record CommandInvocation(NBInvokableCommand command, NBCommandParams params, String contextName) {
}
public static List<CommandInvocation> assemble(List<Cmd> cmds, Function<String, NBBufferedCommandContext> ctxprovider) {
List<Cmd> mappedCmds = tagCommandsWithContext(cmds);
List<CommandInvocation> invocations = prepareMappedPhases(mappedCmds, ctxprovider);
return invocations;
}
private static List<Cmd> tagCommandsWithContext(List<Cmd> cmds) {
LinkedList<Cmd> tagged = new LinkedList<>();
String contextName = Cmd.DEFAULT_TARGET_CONTEXT;
for (Cmd cmd : cmds) {
if (cmd.getArgs().containsKey("context")) {
String ctx = cmd.getArgs().remove("context").getValue();
String step = cmd.getArgs().containsKey("step") ? cmd.getArgs().remove("step").getValue() : "no-step";
tagged.add(cmd.forTargetContext(ctx, step));
} else if (cmd.getCmdType() == CmdType.context) {
contextName = cmd.getArgValue("context_name");
if (contextName.equals(Cmd.DEFAULT_TARGET_CONTEXT)) {
logger.warn("You are explicitly setting the scenario name to " + Cmd.DEFAULT_TARGET_CONTEXT + "'. This is likely an error. " +
"This is the default scenario name, and if you are using different scenario names you should pick something that is different and specific.");
}
} else {
tagged.add(cmd.forTargetContext(contextName, null));
}
}
return new ArrayList<>(tagged);
}
private static List<CommandInvocation> prepareMappedPhases(List<Cmd> mappedCmds, Function<String, NBBufferedCommandContext> ctxProvider) {
List<CommandInvocation> parameterizedInvocations = new ArrayList<>();
NBCoreInvokableResolver core_resolver = new NBCoreInvokableResolver();
String basename = "phase_";
int count = 0;
for (Cmd cmd : mappedCmds) {
count++;
String phaseName = basename + count;
NBCommandParams params = switch (cmd.getCmdType()) {
case indirect, java, context -> NBCommandParams.of(cmd.getArgMap());
default -> NBCommandParams.of(Map.of());
};
String targetContext = cmd.getTargetContext();
NBInvokableCommand command = core_resolver.resolve(cmd, ctxProvider.apply(targetContext), phaseName);
if (command==null) {
throw new BasicError("Found zero commands for spec;" + cmd);
}
String contextName = cmd.getTargetContext();
parameterizedInvocations.add(new CommandInvocation(command, params, contextName));
}
return parameterizedInvocations;
}
// private static NBBaseCommand buildJavascriptCommand(List<Cmd> cmds, String targetScenario, NBBufferedCommandContext parent, String phaseName) {
//// boolean dryrun;
//// NBScriptedScenario.Invocation invocation = dryrun ?
//// NBScriptedScenario.Invocation.RENDER_SCRIPT :
//// NBScriptedScenario.Invocation.EXECUTE_SCRIPT;
//
// final ScriptBuffer buffer = new BasicScriptBuffer().add(cmds.toArray(new Cmd[0]));
// final String scriptData = buffer.getParsedScript();
//
// final NBCommandParams cmdParams = new NBCommandParams();
// cmdParams.putAll(buffer.getCombinedParams());
//
// final NBScriptedCommand cmd = new NBScriptedCommand(parent, phaseName, targetScenario);
//
// cmd.addScriptText(scriptData);
// return cmd;
// }
// private static NBBaseCommand buildJavaCommand(List<Cmd> cmds, NBComponent parent, String phaseName) {
// if (cmds.size() != 1) {
// throw new RuntimeException("java phases require exactly 1 java command");
// }
// Cmd javacmd = cmds.get(0);
// NBBaseCommand cmd = (NBBaseCommand) NBJavaCommandLoader.init(javacmd.getArgValue("_impl"), parent, phaseName, javacmd.getTargetContext());
// return cmd;
// }
}

View File

@@ -0,0 +1,49 @@
/*
* 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.engine.core.lifecycle.session;
import io.nosqlbench.engine.core.lifecycle.scenario.context.NBBufferedCommandContext;
import io.nosqlbench.engine.core.lifecycle.scenario.context.NBCommandContext;
import io.nosqlbench.engine.core.lifecycle.scenario.context.NBCommandParams;
import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBCommandResult;
import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBInvokableCommand;
import io.nosqlbench.nb.api.config.standard.TestComponent;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Map;
import java.util.Objects;
public class NBCommandInvoker {
private final static Logger logger = LogManager.getLogger(NBCommandInvoker.class);
public static NBCommandResult invoke(NBInvokableCommand command, NBCommandParams params) {
return invoke(createContext(),command,params);
}
public static NBCommandResult invoke(NBBufferedCommandContext context, NBInvokableCommand command) {
return invoke(context, command, NBCommandParams.of(Map.of()));
}
private static NBBufferedCommandContext createContext() {
return NBCommandContext.builder().name("testing").build(TestComponent.EMPTY_COMPONENT);
}
public static NBCommandResult invoke(NBBufferedCommandContext context, NBInvokableCommand command, NBCommandParams params) {
return command.invokeSafe(context,params);
}
}

View File

@@ -0,0 +1,69 @@
/*
* 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.engine.core.lifecycle.session;
import io.nosqlbench.engine.cmdstream.Cmd;
import io.nosqlbench.engine.cmdstream.CmdType;
import io.nosqlbench.engine.core.lifecycle.scenario.context.NBBufferedCommandContext;
import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBInvokableCommand;
import io.nosqlbench.nb.annotations.Service;
import io.nosqlbench.nb.api.components.NBComponent;
import java.util.*;
/**
* This is the core wrapper around any resolvers for NB commands.
* Call it directly and it will invoke all others in the runtime as long as they
* are registered as a service for SPI.
*/
public class NBCoreInvokableResolver implements NBInvokableResolver {
private final static String[] precedence = new String[]{"js", "java", "autojs"};
private SequencedMap<String,NBInvokableResolver> resolvers = new LinkedHashMap<>();
@Override
public NBInvokableCommand resolve(Cmd cmd, NBBufferedCommandContext parent, String phaseName) {
for (NBInvokableResolver resolver : getResolvers().values()) {
NBInvokableCommand loadedCommand = resolver.resolve(cmd, parent, phaseName);
if (loadedCommand!=null) {
return loadedCommand;
}
}
// if (cmd.getCmdType() == CmdType.indirect) {
// }
return null;
}
private SequencedMap<String, NBInvokableResolver> getResolvers() {
if (this.resolvers == null || this.resolvers.isEmpty()) {
SequencedMap<String,NBInvokableResolver> resolverMap = new LinkedHashMap<>();
ServiceLoader<NBInvokableResolver> resolvers = ServiceLoader.load(NBInvokableResolver.class);
for (NBInvokableResolver resolver : resolvers) {
String selector = resolver.getClass().getAnnotation(Service.class).selector();
resolverMap.put(selector,resolver);
}
for (int i = precedence.length-1; i >= 0; i--) {
if (resolverMap.containsKey(precedence[i])) {
NBInvokableResolver resolver = resolverMap.remove(precedence[i]);
resolverMap.putFirst(precedence[i],resolver);
}
}
this.resolvers = resolverMap;
}
return this.resolvers;
}
}

View File

@@ -0,0 +1,44 @@
/*
* 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.engine.core.lifecycle.session;
import io.nosqlbench.engine.cmdstream.BasicScriptBuffer;
import io.nosqlbench.engine.cmdstream.Cmd;
import io.nosqlbench.engine.core.lifecycle.scenario.context.NBBufferedCommandContext;
import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBInvokableCommand;
import io.nosqlbench.engine.core.lifecycle.scenario.script.NBScriptedCommand;
import io.nosqlbench.nb.annotations.Service;
import java.util.List;
@Service(value = NBInvokableResolver.class, selector = "js")
public class NBScriptCommandResolver implements NBInvokableResolver {
@Override
public NBInvokableCommand resolve(Cmd cmd, NBBufferedCommandContext parent, String phaseName) {
return switch (cmd.getCmdType()) {
case run, await, forceStop, stop, start, waitMillis, fragment, script->
new NBScriptedCommand(parent, phaseName, cmd.getTargetContext()).add(cmd);
// case fragment ->
// new NBScriptedCommand(parent, phaseName, cmd.getTargetContext()).addScriptText(cmd.getArgValue("fragment"));
// case script ->
// new NBScriptedCommand(parent, phaseName, cmd.getTargetContext()).addScriptFiles(cmd.getArgValue("path"));
default -> null;
};
}
}

View File

@@ -16,32 +16,24 @@
package io.nosqlbench.engine.core.lifecycle.session;
import io.nosqlbench.api.engine.metrics.instruments.NBFunctionGauge;
import io.nosqlbench.api.engine.metrics.instruments.NBMetricGauge;
import io.nosqlbench.api.labels.NBLabeledElement;
import io.nosqlbench.api.labels.NBLabels;
import io.nosqlbench.components.NBComponent;
import io.nosqlbench.components.NBBaseComponent;
import io.nosqlbench.components.NBComponentSubScope;
import io.nosqlbench.components.decorators.NBTokenWords;
import io.nosqlbench.engine.cli.BasicScriptBuffer;
import io.nosqlbench.engine.cli.Cmd;
import io.nosqlbench.engine.cli.ScriptBuffer;
import io.nosqlbench.nb.api.engine.activityimpl.ActivityDef;
import io.nosqlbench.nb.api.engine.metrics.instruments.NBFunctionGauge;
import io.nosqlbench.nb.api.engine.metrics.instruments.NBMetricGauge;
import io.nosqlbench.nb.api.labels.NBLabeledElement;
import io.nosqlbench.nb.api.labels.NBLabels;
import io.nosqlbench.nb.api.components.NBBaseComponent;
import io.nosqlbench.nb.api.components.decorators.NBTokenWords;
import io.nosqlbench.engine.cmdstream.Cmd;
import io.nosqlbench.engine.core.clientload.*;
import io.nosqlbench.engine.core.lifecycle.ExecutionResult;
import io.nosqlbench.engine.core.lifecycle.process.NBCLIErrorHandler;
import io.nosqlbench.engine.core.lifecycle.scenario.context.ScenarioParams;
import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBScenario;
import io.nosqlbench.engine.core.lifecycle.scenario.execution.ScenariosExecutor;
import io.nosqlbench.engine.core.lifecycle.scenario.execution.ScenariosResults;
import io.nosqlbench.engine.core.lifecycle.scenario.script.NBScriptedScenario;
import io.nosqlbench.engine.core.lifecycle.scenario.context.NBBufferedCommandContext;
import io.nosqlbench.engine.core.lifecycle.scenario.context.NBCommandContext;
import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBCommandResult;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
/**
@@ -55,20 +47,33 @@ public class NBSession extends NBBaseComponent implements Function<List<Cmd>, Ex
private final String sessionName;
private final ClientSystemMetricChecker clientMetricChecker;
private final Map<String, NBBufferedCommandContext> contexts = new ConcurrentHashMap<>();
public enum STATUS {
OK,
WARNING,
ERROR
}
private NBBufferedCommandContext getContext(String name) {
return contexts.computeIfAbsent(
name,
n -> NBCommandContext.builder().name(n).build(this)
);
}
public NBSession(
NBLabeledElement labelContext,
String sessionName
) {
super(null, labelContext.getLabels().and("session", sessionName));
super(
null,
labelContext.getLabels()
.and("session", sessionName)
);
this.sessionName = sessionName;
this.clientMetricChecker = new ClientSystemMetricChecker(this, NBLabels.forKV(),10);
this.clientMetricChecker = new ClientSystemMetricChecker(this, NBLabels.forKV(), 10);
registerLoadAvgMetrics();
registerMemInfoMetrics();
// registerDiskStatsMetrics();
@@ -78,109 +83,55 @@ public class NBSession extends NBBaseComponent implements Function<List<Cmd>, Ex
}
/**
* Notes on scenario names:
* <UL>
* <LI>If none are provided, then all cmds are implicitly allocated to the "default" scenario.</LI>
* <LI>If the name "default" is provided directly, then this is considered an error.</LI>
* <LI>Otherwise, the most recently set scenario name is the one in which all following commands are run.</LI>
* <LI></LI>
* </UL>
*
* @param cmds
* the function argument
* @return
*/
public ExecutionResult apply(List<Cmd> cmds) {
if (cmds.isEmpty()) {
logger.info("No commands provided.");
}
Map<String, String> params = new CmdParamsBuffer(cmds).getGlobalParams();
// TODO: add context closing command
// TODO: inject context closing commands after the last command referencing each context
List<NBCommandAssembly.CommandInvocation> invocationCalls = NBCommandAssembly.assemble(cmds, this::getContext);
ResultCollector collector = new ResultCollector();
try (ResultContext results = new ResultContext(collector)) {
final ScenariosExecutor scenariosExecutor = new ScenariosExecutor(this, "executor-" + sessionName, 1);
NBScenario scenario;
if (cmds.get(0).getCmdType().equals(Cmd.CmdType.java)) {
scenario = buildJavaScenario(cmds);
} else {
scenario = buildJavacriptScenario(cmds);
try (ResultContext results = new ResultContext(collector).ok()) {
for (NBCommandAssembly.CommandInvocation invocation : invocationCalls) {
try {
String targetContext = invocation.contextName();
NBBufferedCommandContext context = getContext(targetContext);
NBCommandResult cmdResult = context.apply(invocation.command(), invocation.params());
results.apply(cmdResult);
} catch (Exception e) {
String msg = "While running command '" + invocation.command() + "' in context '" + invocation.contextName() + "', an error occurred: " + e.toString();
logger.error(msg);
results.error(e);
break;
}
}
try (NBComponentSubScope scope = new NBComponentSubScope(scenario)) {
scenariosExecutor.execute(scenario, params);
// this.doReportSummaries(this.reportSummaryTo, this.result);
} catch (Exception e) {
results.error(e);
}
final ScenariosResults scenariosResults = scenariosExecutor.awaitAllResults();
logger.debug(() -> "Total of " + scenariosResults.getSize() + " result object returned from ScenariosExecutor");
// logger.info(scenariosResults.getExecutionSummary());
if (scenariosResults.hasError()) {
results.error(scenariosResults.getAnyError().orElseThrow());
final Exception exception = scenariosResults.getOne().getException();
logger.warn(scenariosResults.getExecutionSummary());
NBCLIErrorHandler.handle(exception, true);
System.err.println(exception.getMessage()); // TODO: make this consistent with ConsoleLogging sequencing
}
results.output(scenariosResults.getExecutionSummary());
results.ok();
}
for (String ctxName : contexts.keySet()) {
NBBufferedCommandContext ctx = contexts.get(ctxName);
logger.debug("awaiting end of activities in context '" + ctxName + "':" +
ctx.controller().getActivityDefs().stream().map(ActivityDef::getAlias).toList());
ctx.controller().shutdown();
ctx.controller().awaitCompletion(Long.MAX_VALUE);
logger.debug("completed");
}
return collector.toExecutionResult();
}
private NBScenario buildJavacriptScenario(List<Cmd> cmds) {
// boolean dryrun;
// NBScriptedScenario.Invocation invocation = dryrun ?
// NBScriptedScenario.Invocation.RENDER_SCRIPT :
// NBScriptedScenario.Invocation.EXECUTE_SCRIPT;
final ScriptBuffer buffer = new BasicScriptBuffer().add(cmds.toArray(new Cmd[0]));
final String scriptData = buffer.getParsedScript();
final ScenarioParams scenarioParams = new ScenarioParams();
scenarioParams.putAll(buffer.getCombinedParams());
final NBScriptedScenario scenario = new NBScriptedScenario(sessionName, this);
scenario.addScriptText(scriptData);
return scenario;
}
private NBScenario buildJavaScenario(List<Cmd> cmds) {
if (cmds.size() != 1) {
throw new RuntimeException("java scenarios require exactly 1 java command");
}
Cmd javacmd = cmds.get(0);
String mainClass = javacmd.getArg("main_class");
// This doesn't work as expected; The newest service loader docs are vague about Provider and no-args ctor requirements
// and the code suggests that you still have to have one unless you are in a named module
// SimpleServiceLoader<NBScenario> loader = new SimpleServiceLoader<>(NBScenario.class, Maturity.Any);
// List<SimpleServiceLoader.Component<? extends NBScenario>> namedProviders = loader.getNamedProviders(mainClass);
// SimpleServiceLoader.Component<? extends NBScenario> provider = namedProviders.get(0);
// Class<? extends NBScenario> type = provider.provider.type();
Class<NBScenario> type;
try {
type = (Class<NBScenario>) Class.forName(mainClass);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
try {
Constructor<? extends NBScenario> constructor = type.getConstructor(NBComponent.class, String.class);
NBScenario scenario = constructor.newInstance(this, sessionName);
return scenario;
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
private void registerLoadAvgMetrics() {
LoadAvgReader reader = new LoadAvgReader();
if (!reader.fileExists())
@@ -203,18 +154,18 @@ public class NBSession extends NBBaseComponent implements Function<List<Cmd>, Ex
if (!reader.fileExists())
return;
NBMetricGauge memTotalGauge = create().gauge("mem_total",reader::getMemTotalkB);
NBMetricGauge memUsedGauge = create().gauge("mem_used",reader::getMemUsedkB);
NBMetricGauge memFreeGauge = create().gauge("mem_free",reader::getMemFreekB);
NBMetricGauge memAvailableGauge = create().gauge("mem_avaialble",reader::getMemAvailablekB);
NBMetricGauge memCachedGauge = create().gauge("mem_cache",reader::getMemCachedkB);
NBMetricGauge memTotalGauge = create().gauge("mem_total", reader::getMemTotalkB);
NBMetricGauge memUsedGauge = create().gauge("mem_used", reader::getMemUsedkB);
NBMetricGauge memFreeGauge = create().gauge("mem_free", reader::getMemFreekB);
NBMetricGauge memAvailableGauge = create().gauge("mem_avaialble", reader::getMemAvailablekB);
NBMetricGauge memCachedGauge = create().gauge("mem_cache", reader::getMemCachedkB);
NBMetricGauge memBufferedGauge = create().gauge("mem_buffered", reader::getMemBufferskB);
// add checking for percent memory used at some given time; TODO: Modify percent threshold
clientMetricChecker.addRatioMetricToCheck(memUsedGauge, memTotalGauge, 90.0, false);
NBMetricGauge swapTotalGauge = create().gauge("swap_total", reader::getSwapTotalkB);
NBMetricGauge swapFreeGauge = create().gauge("swap_free",reader::getSwapFreekB);
NBMetricGauge swapUsedGauge = create().gauge("swap_used",reader::getSwapUsedkB);
NBMetricGauge swapFreeGauge = create().gauge("swap_free", reader::getSwapFreekB);
NBMetricGauge swapUsedGauge = create().gauge("swap_used", reader::getSwapUsedkB);
}
private void registerDiskStatsMetrics() {
@@ -223,9 +174,9 @@ public class NBSession extends NBBaseComponent implements Function<List<Cmd>, Ex
return;
for (String device : reader.getDevices()) {
create().gauge(device +"_transactions", () ->reader.getTransactionsForDevice(device));
create().gauge(device +"_kB_read", () -> reader.getKbReadForDevice(device));
create().gauge(device+"_kB_written", () -> reader.getKbWrittenForDevice(device));
create().gauge(device + "_transactions", () -> reader.getTransactionsForDevice(device));
create().gauge(device + "_kB_read", () -> reader.getKbReadForDevice(device));
create().gauge(device + "_kB_written", () -> reader.getKbWrittenForDevice(device));
}
}
@@ -234,10 +185,10 @@ public class NBSession extends NBBaseComponent implements Function<List<Cmd>, Ex
if (!reader.fileExists())
return;
for (String iface : reader.getInterfaces()) {
create().gauge(iface+"_rx_bytes",() -> reader.getBytesReceived(iface));
create().gauge(iface+"_rx_packets",() -> reader.getPacketsReceived(iface));
create().gauge(iface+"_tx_bytes",() -> reader.getBytesTransmitted(iface));
create().gauge(iface+"_tx_packets",() -> reader.getPacketsTransmitted(iface));
create().gauge(iface + "_rx_bytes", () -> reader.getBytesReceived(iface));
create().gauge(iface + "_rx_packets", () -> reader.getPacketsReceived(iface));
create().gauge(iface + "_tx_bytes", () -> reader.getBytesTransmitted(iface));
create().gauge(iface + "_tx_packets", () -> reader.getPacketsTransmitted(iface));
}
}
@@ -246,7 +197,7 @@ public class NBSession extends NBBaseComponent implements Function<List<Cmd>, Ex
if (!reader.fileExists())
return;
NBMetricGauge cpuUserGauge = create().gauge("cpu_user", reader::getUserTime);
NBMetricGauge cpuSystemGauge = create().gauge("cpu_system",reader::getSystemTime);
NBMetricGauge cpuSystemGauge = create().gauge("cpu_system", reader::getSystemTime);
NBMetricGauge cpuIdleGauge = create().gauge("cpu_idle", reader::getIdleTime);
NBMetricGauge cpuIoWaitGauge = create().gauge("cpu_iowait", reader::getIoWaitTime);
NBMetricGauge cpuTotalGauge = create().gauge("cpu_total", reader::getTotalTime);

View File

@@ -17,14 +17,16 @@
package io.nosqlbench.engine.core.lifecycle.session;
import io.nosqlbench.engine.core.lifecycle.ExecutionResult;
import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBCommandResult;
import java.util.function.Consumer;
import java.util.function.Supplier;
public class ResultContext implements AutoCloseable {
private final Consumer<ResultContext> receiver;
private ExecutionResult.Status status;
ResultContext(Consumer<ResultContext> receiver) {
public ResultContext(Consumer<ResultContext> receiver) {
this.receiver = receiver;
}
@@ -33,12 +35,15 @@ public class ResultContext implements AutoCloseable {
public final StringBuilder buf = new StringBuilder();
private long stopMillis;
public void error(Exception error) {
public ResultContext error(Exception error) {
this.error = error;
error();
return this;
}
public void output(CharSequence cs) {
public ResultContext output(CharSequence cs) {
buf.append(cs);
return this;
}
public String output() {
return buf.toString();
@@ -60,11 +65,13 @@ public class ResultContext implements AutoCloseable {
return new ExecutionResult(this.startMillis,this.stopMillis,buf.toString(), error);
}
public void ok() {
public ResultContext ok() {
this.status= ExecutionResult.Status.OK;
return this;
}
public void error() {
public ResultContext error() {
this.status= ExecutionResult.Status.ERROR;
return this;
}
public long startMillis() {
@@ -78,4 +85,15 @@ public class ResultContext implements AutoCloseable {
public Exception getException() {
return error;
}
public ResultContext apply(Supplier<ExecutionResult> resultSource) {
ExecutionResult executionResult = resultSource.get();
if (executionResult.getException()!=null) {
this.error(executionResult.getException());
output(executionResult.getIOLog());
} else {
this.ok();
}
return this;
}
}

View File

@@ -16,7 +16,7 @@
package io.nosqlbench.engine.core.logging;
import io.nosqlbench.api.logging.NBLogLevel;
import io.nosqlbench.nb.api.logging.NBLogLevel;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.core.Filter;
@@ -198,10 +198,10 @@ public class LoggerConfig extends ConfigurationFactory {
LayoutComponentBuilder logfileLayout = builder.newLayout("PatternLayout")
.addAttribute("pattern", logfilePattern);
String filebase = getSessionName().replaceAll("\\s", "_");
String logfilePath = loggerDir.resolve(filebase + ".log").toString();
String logfilePath = loggerDir.resolve(getFileBase() + ".log").toString();
this.logfileLocation = logfilePath;
String archivePath = loggerDir.resolve(filebase + "-TIMESTAMP.log.gz").toString()
String archivePath = loggerDir.resolve(getFileBase() + "-TIMESTAMP.log.gz").toString()
.replaceAll("TIMESTAMP", "%d{MM-dd-yy}");
ComponentBuilder triggeringPolicy = builder.newComponent("Policies")
@@ -218,8 +218,12 @@ public class LoggerConfig extends ConfigurationFactory {
builder.add(logsAppenderBuilder);
if (isDedicatedVerificationLoggerEnabled) {
var verificationLogfilePath = loggerDir.resolve(filebase + "_verification.log").toString();
addResultVerificationLoggingChannel(builder, verificationLogfilePath);
attachAuxLogger(builder, "VERIFY", fileLevel);
}
// TODO: build stop-words transcoder, add padding to end of alphabet, substitute stopwords
if (fileLevel.isInRange(Level.INFO,Level.TRACE)) {
attachAuxLogger(builder, "RUNTIME", fileLevel);
}
rootBuilder.add(
@@ -256,6 +260,10 @@ public class LoggerConfig extends ConfigurationFactory {
return builtConfig;
}
private String getFileBase() {
return getSessionName().replaceAll("\\s", "_");
}
private String getSessionName() {
return sessionName;
}
@@ -381,22 +389,23 @@ public class LoggerConfig extends ConfigurationFactory {
return this;
}
private void addResultVerificationLoggingChannel(ConfigurationBuilder<BuiltConfiguration> builder, String verificationLogfilePath) {
var appenderName = "RESULTVERIFYLOG";
private void attachAuxLogger(ConfigurationBuilder<BuiltConfiguration> builder, String loggerName, Level fileLevel) {
String appenderName = loggerName+(("_APPENDER").toUpperCase());
String fileName = loggerDir.resolve(getFileBase() + "_"+loggerName+".log").toString().toLowerCase();
var appender = builder
.newAppender(appenderName, FileAppender.PLUGIN_NAME)
.addAttribute("append", false)
.addAttribute("fileName", verificationLogfilePath)
.addAttribute("fileName", fileName)
.add(builder
.newLayout("PatternLayout")
.addAttribute("pattern", "%d %p %C{1.} [%t] %m%n")
);
var logger = builder
.newLogger("VERIFY", Level.INFO)
.newLogger(loggerName, fileLevel)
.add(builder.newAppenderRef(appenderName))
.addAttribute("additivity", false);
builder.add(appender);
builder.add(logger);
}
}

View File

@@ -16,11 +16,11 @@
package io.nosqlbench.engine.core.metadata;
import io.nosqlbench.api.config.standard.TestComponent;
import io.nosqlbench.api.content.Content;
import io.nosqlbench.api.content.NBIO;
import io.nosqlbench.api.engine.activityimpl.ActivityDef;
import io.nosqlbench.api.errors.BasicError;
import io.nosqlbench.nb.api.config.standard.TestComponent;
import io.nosqlbench.nb.api.nbio.Content;
import io.nosqlbench.nb.api.nbio.NBIO;
import io.nosqlbench.nb.api.engine.activityimpl.ActivityDef;
import io.nosqlbench.nb.api.errors.BasicError;
import io.nosqlbench.engine.api.activityapi.core.ActivityType;
import io.nosqlbench.engine.core.lifecycle.activity.ActivityTypeLoader;
import io.nosqlbench.nb.annotations.Service;

View File

@@ -16,10 +16,10 @@
package io.nosqlbench.engine.core.metrics;
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.nb.api.annotations.Annotation;
import io.nosqlbench.nb.api.annotations.Annotator;
import io.nosqlbench.nb.api.config.standard.*;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

View File

@@ -16,8 +16,8 @@
package io.nosqlbench.engine.core;
import io.nosqlbench.api.config.standard.TestComponent;
import io.nosqlbench.api.engine.activityimpl.ActivityDef;
import io.nosqlbench.nb.api.config.standard.TestComponent;
import io.nosqlbench.nb.api.engine.activityimpl.ActivityDef;
import io.nosqlbench.engine.api.activityapi.core.*;
import io.nosqlbench.engine.api.activityapi.input.Input;
import io.nosqlbench.engine.api.activityapi.input.InputDispenser;

View File

@@ -16,8 +16,8 @@
package io.nosqlbench.engine.core;
import io.nosqlbench.api.config.standard.TestComponent;
import io.nosqlbench.api.engine.activityimpl.ActivityDef;
import io.nosqlbench.nb.api.config.standard.TestComponent;
import io.nosqlbench.nb.api.engine.activityimpl.ActivityDef;
import io.nosqlbench.engine.api.activityapi.core.Action;
import io.nosqlbench.engine.api.activityapi.core.Activity;
import io.nosqlbench.engine.api.activityapi.core.Motor;

View File

@@ -0,0 +1,58 @@
/*
* 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.
* 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.engine.core;
import io.nosqlbench.engine.cmdstream.Cmd;
import io.nosqlbench.engine.cmdstream.CmdArg;
import io.nosqlbench.engine.cmdstream.CmdParam;
import io.nosqlbench.engine.core.lifecycle.session.NBCommandInvoker;
import io.nosqlbench.engine.core.lifecycle.session.NBSession;
import io.nosqlbench.nb.api.config.standard.TestComponent;
import io.nosqlbench.nb.api.components.NBComponent;
import io.nosqlbench.engine.core.lifecycle.scenario.context.NBBufferedCommandContext;
import io.nosqlbench.engine.core.lifecycle.scenario.context.NBCommandContext;
import io.nosqlbench.engine.core.lifecycle.scenario.context.NBCommandParams;
import io.nosqlbench.engine.core.lifecycle.scenario.execution.NBCommandResult;
import io.nosqlbench.engine.core.lifecycle.scenario.script.NBScriptedCommand;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.jupiter.api.Test;
import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
public class NBBaseCommandTest {
private final Logger logger = LogManager.getLogger(NBBaseCommandTest.class);
@Test
public void shouldLoadScriptText() {
NBBufferedCommandContext ctx = NBCommandContext.builder().name("testing").build(NBComponent.EMPTY_COMPONENT);
NBScriptedCommand cmd = NBScriptedCommand.ofScripted("testing", Map.of(),ctx, NBScriptedCommand.Invocation.EXECUTE_SCRIPT);
cmd.add(new Cmd("fragment",Map.of(
"fragment",new CmdArg(new CmdParam("fragment",s->s,false),"=","print('loaded script environment...');")
)));
try {
NBCommandResult result = NBCommandInvoker.invoke(ctx,cmd);
assertThat(result.getIOLog()).contains("loaded script environment...");
} catch (Exception e) {
logger.debug(() -> "Scenario run encountered an exception: " + e.getMessage());
}
}
}

View File

@@ -1,49 +0,0 @@
/*
* 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.
* 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.engine.core;
import io.nosqlbench.api.config.standard.TestComponent;
import io.nosqlbench.engine.api.scripting.ScriptEnvBuffer;
import io.nosqlbench.engine.core.lifecycle.scenario.execution.ScenarioResult;
import io.nosqlbench.engine.core.lifecycle.scenario.execution.ScenariosExecutor;
import io.nosqlbench.engine.core.lifecycle.scenario.script.NBScriptedScenario;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.junit.jupiter.api.Test;
import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
public class NBScenarioTest {
private final Logger logger = LogManager.getLogger(NBScenarioTest.class);
@Test
public void shouldLoadScriptText() {
NBScriptedScenario scenario = NBScriptedScenario.ofScripted("testing", Map.of(),new TestComponent(), NBScriptedScenario.Invocation.EXECUTE_SCRIPT);
scenario.addScriptText("print('loaded script environment...');\n");
try {
ScenariosExecutor executor = new ScenariosExecutor(TestComponent.INSTANCE, "test", 1);
executor.execute(scenario,Map.of());
ScenarioResult result = executor.awaitAllResults().getOne();
assertThat(result.getIOLog()).contains("loaded script environment...");
} catch (Exception e) {
logger.debug(() -> "Scenario run encountered an exception: " + e.getMessage());
}
}
}

View File

@@ -0,0 +1,98 @@
/*
* 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.engine.core.lifecycle.session;
import io.nosqlbench.engine.cmdstream.Cmd;
import io.nosqlbench.engine.cmdstream.CmdType;
import io.nosqlbench.nb.api.errors.BasicError;
import org.junit.jupiter.api.Test;
import java.util.LinkedList;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.*;
// test for dots and underscores in names
class CmdParserTest {
@Test
public void testSingleCommand() {
List<Cmd> cmds = CmdParser.parse("testcmd42");
assertThat(cmds).hasSize(1);
assertThat(cmds.get(0).getCmdType()).isEqualTo(CmdType.indirect);
assertThat(cmds.get(0).getArgValue("_impl")).isEqualTo("testcmd42");
}
@Test
public void testSingleCommandWithArgs() {
List<Cmd> cmds = CmdParser.parse("testcmd43 param1=value1");
assertThat(cmds).hasSize(1);
assertThat(cmds.get(0).getCmdType()).isEqualTo(CmdType.indirect);
assertThat(cmds.get(0).getArgValue("_impl")).isEqualTo("testcmd43");
assertThat(cmds.get(0).getArgValue("param1")).isEqualTo("value1");
}
@Test
public void testSingleDquotedArg() {
List<Cmd> cmds = CmdParser.parse("testcmd44 param1=\"value1\"");
assertThat(cmds).hasSize(1);
assertThat(cmds.get(0).getCmdType()).isEqualTo(CmdType.indirect);
assertThat(cmds.get(0).getArgValue("_impl")).isEqualTo("testcmd44");
assertThat(cmds.get(0).getArgValue("param1")).isEqualTo("value1");
}
@Test
public void testSpecialSymbolValue() {
List<Cmd> cmds = CmdParser.parse("start param1="+ CmdParser.SYMBOLS+ " param2='"+ CmdParser.SYMBOLS+ "' param3=\""+ CmdParser.SYMBOLS+ "\"");
assertThat(cmds).hasSize(1);
assertThat(cmds.get(0).getCmdType()).isEqualTo(CmdType.start);
assertThat(cmds.get(0).getArgValue("param1")).isEqualTo(CmdParser.SYMBOLS);
assertThat(cmds.get(0).getArgValue("param2")).isEqualTo(CmdParser.SYMBOLS);
assertThat(cmds.get(0).getArgValue("param3")).isEqualTo(CmdParser.SYMBOLS);
}
@Test
public void testCatchesShortReadErrors() {
assertThrows(BasicError.class,() -> CmdParser.parse("start param1=\"shortread"),
"an error should be thrown if end of input is reached in the middle of a double-quoted value.");
assertThrows(BasicError.class,() -> CmdParser.parse("start param1='shortread"),
"an error should be thrown if end of input is reached in the middle of a single-quoted value.");
assertThrows(BasicError.class,() -> CmdParser.parse("param1=value1"),
"an error should be thrown if a named parameter is specified without a prior command.");
}
@Test
public void testThatSymbolsAreQuotedInStringForm() {
List<Cmd> cmds = CmdParser.parse("start param1=value1 param2='~should be quoted'");
assertThat(cmds.size()).isEqualTo(1);
assertThat(cmds.get(0).getCmdType()).isEqualTo(CmdType.start);
assertThat(cmds.get(0).getArgValue("param1")).isEqualTo("value1");
assertThat(cmds.get(0).getArgValue("param2")).isEqualTo("~should be quoted");
assertThat(cmds.get(0).toString()).isEqualTo("start param1=value1 param2='~should be quoted'");
}
@Test
public void testBasicArgvParser() {
LinkedList<Cmd> cmds = CmdParser.parseArgvCommands(new LinkedList<>(List.of("_cmd4", "param1=value1")));
assertThat(cmds.size()).isEqualTo(1);
assertThat(cmds.get(0).getCmdType()).isEqualTo(CmdType.indirect);
assertThat(cmds.get(0).getArgValue("_impl")).isEqualTo("_cmd4");
assertThat(cmds.get(0).getArgValue("param1")).isEqualTo("value1");
}
}

View File

@@ -17,8 +17,8 @@
package io.nosqlbench.engine.core.metrics;
import com.codahale.metrics.Timer;
import io.nosqlbench.api.labels.NBLabels;
import io.nosqlbench.api.engine.metrics.DeltaHdrHistogramReservoir;
import io.nosqlbench.nb.api.labels.NBLabels;
import io.nosqlbench.nb.api.engine.metrics.DeltaHdrHistogramReservoir;
import org.junit.jupiter.api.Test;
import java.util.concurrent.TimeUnit;

View File

@@ -1,42 +0,0 @@
/*
* 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.
* 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.engine.core.script;
import io.nosqlbench.api.config.standard.TestComponent;
import io.nosqlbench.engine.core.lifecycle.scenario.execution.ScenariosExecutor;
import io.nosqlbench.engine.core.lifecycle.scenario.execution.ScenariosResults;
import io.nosqlbench.engine.core.lifecycle.scenario.script.NBScriptedScenario;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import java.util.Map;
public class ScenariosExecutorTest {
@Test
@Disabled
public void testAwaitOnTime() {
ScenariosExecutor e = new ScenariosExecutor(new TestComponent("id","test-await-on-time"),ScenariosExecutorTest.class.getSimpleName(), 1);
NBScriptedScenario scenario = NBScriptedScenario.ofScripted("testing", Map.of(),new TestComponent("scripted-scenario","scripted-scenario"), NBScriptedScenario.Invocation.EXECUTE_SCRIPT);
scenario.addScriptText("load('classpath:scripts/asyncs.js');\nsetTimeout(\"print('waited')\",5000);\n");
e.execute(scenario,Map.of());
ScenariosResults scenariosResults = e.awaitAllResults();
}
}