Merge branch 'nosqlbench-1001-nfe-init' into add-tags-script

This commit is contained in:
Jonathan Shook 2023-02-05 21:21:13 -06:00
commit d8d7bb61ab
15 changed files with 186 additions and 141 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 nosqlbench
* Copyright (c) 2022-2023 nosqlbench
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -166,7 +166,7 @@ public abstract class BaseDriverAdapter<R extends Op, S> implements DriverAdapte
.add(Param.optional("instrument", Boolean.class))
.add(Param.optional(List.of("workload", "yaml"), String.class, "location of workload yaml file"))
.add(Param.optional("driver", String.class))
.add(Param.defaultTo("dryrun",false))
.add(Param.defaultTo("dryrun","none").setRegex("(op|jsonnet|none)"))
.asReadOnly();
}

View File

@ -19,6 +19,6 @@ However, there are other ways to feed an activity. All inputs are
modular within the nosqlbench runtime. To see what inputs are
available, you can simpy run:
PROG --list-input-types
${PROG} --list-input-types
Any input listed this way should have its own documentation.

View File

@ -14,7 +14,7 @@ All cycle logfiles have the *.cyclelog* suffix.
You can dump an rlefile to the screen to see the content in text form by
running a command like this:
PROG --export-cycle-log <filename> [spans|cycles]
${PROG} --export-cycle-log <filename> [spans|cycles]
You do not need to specify the extension. If you do not specify either
optional format at the end, then *spans* is assumed. It will print output
@ -40,7 +40,7 @@ If you need to modify and then re-use a cycle log, you can do this with
simple text tools. Once you have modified the file, you can import it back
to the native format with:
PROG --import-cycle-log <infile.txt> <outfile.cyclelog>
${PROG} --import-cycle-log <infile.txt> <outfile.cyclelog>
The importer recognizes both formats listed above.

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 nosqlbench
* Copyright (c) 2022-2023 nosqlbench
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -35,27 +35,27 @@ public enum RunState {
* Initial state after creation of a motor. This is the initial state upon instantiation of a motor, before
* it is called on to do any active logic besides what occurs in the constructor.
*/
Uninitialized("i"),
Uninitialized(""),
/**
* A thread has been invoked, but is initializing and preparing for its main control loop.
* This is signaled <EM>by the motor</EM> after {@link Runnable#run}, but before entering the main processing
* loop.
*/
Starting("s"),
Starting(""),
/**
* A thread is iterating within the main control loop.
* This is signaled <EM>by the motor</EM> once initialization in the main loop is complete and immediately
* before it enters it's main processing loop.
*/
Running("R\u23F5"),
Running("\u23F5"),
/**
* <P>The thread has been requested to stop. This can be set by a managing thread which is not the
* motor thread, or by the motor thread. In either case, the motor thread is required to observe changes to this and initiate shutdown.</P>
*/
Stopping("s"),
Stopping(""),
/**
* The thread has stopped. This should only be set by the motor. This state will only be visible
@ -64,7 +64,7 @@ public enum RunState {
* <P>NOTE: When a motor is stopped or finished, its state will remain visible in state tracking until
* {@link Motor#getState()}.{@link MotorState#removeState()} is called.</P>
*/
Stopped("e\u23F9"),
Stopped("\u23F9"),
/**
* <P>A thread has exhausted its supply of values on the input (AKA cycles), thus has completed its work.
@ -73,12 +73,12 @@ public enum RunState {
* <P>NOTE: When a motor is stopped or finished, its state will remain visible in state tracking until
* {@link Motor#getState()}.{@link MotorState#removeState()} is called.</P>
*/
Finished("F"),
Finished(""),
/**
* If a motor has seen an exception, it goes into errored state before propagating the error.
*/
Errored("E");
Errored("");
private final String runcode;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 nosqlbench
* Copyright (c) 2022-2023 nosqlbench
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -22,4 +22,12 @@ public interface Stoppable {
* completes, the request will cause the component to stop cooperatively.
*/
void requestStop();
static void stop(Object... candidates) {
for (Object candidate : candidates) {
if (candidate instanceof Stoppable stoppable) {
stoppable.requestStop();
}
}
}
}

View File

@ -495,7 +495,8 @@ public class SimpleActivity implements Activity, ProgressCapable, ActivityDefObs
logger.info(() -> "skipped mapping op '" + pop.getName() + "'");
continue;
}
boolean dryrun = pop.takeStaticConfigOr("dryrun", false);
String dryrunSpec = pop.takeStaticConfigOr("dryrun", "none");
boolean dryrun = dryrunSpec.equalsIgnoreCase("op");
DriverAdapter adapter = adapters.get(i);
OpMapper opMapper = adapter.getOpMapper();

View File

@ -463,12 +463,7 @@ public class CoreMotor<D> implements ActivityDefObserver, Motor<D>, Stoppable {
public synchronized void requestStop() {
RunState currentState = motorState.get();
if (Objects.requireNonNull(currentState) == Running) {
if (input instanceof Stoppable) {
((Stoppable) input).requestStop();
}
if (action instanceof Stoppable) {
((Stoppable) action).requestStop();
}
Stoppable.stop(input, action);
motorState.enterState(Stopping);
} else {
logger.warn(() -> "attempted to stop motor " + this.getSlotId() + ": from non Running state:" + currentState);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 nosqlbench
* Copyright (c) 2022-2023 nosqlbench
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -20,6 +20,8 @@ import io.nosqlbench.engine.api.activityapi.core.RunState;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Arrays;
/**
* A value type which encodes the atomic state of a RunState tally.
*/
@ -51,6 +53,28 @@ public class RunStateImage {
return true;
}
public boolean isNoneOther(RunState... runStates) {
int[] scan = Arrays.copyOf(counts, counts.length);
for (RunState runState : runStates) {
scan[runState.ordinal()]=0;
}
for (int i : scan) {
if (i>0) {
return false;
}
}
return true;
}
public RunState getMinState() {
for (int ord = 0; ord < counts.length - 1; ord++) {
if (counts[ord] > 0) {
return RunState.values()[ord];
}
}
throw new RuntimeException("There were zero states, so min state is undefined");
}
public RunState getMaxState() {
for (int ord = counts.length - 1; ord >= 0; ord--) {
if (counts[ord] > 0) {
@ -63,7 +87,7 @@ public class RunStateImage {
public String toString() {
StringBuilder sb = new StringBuilder();
for (RunState runState : RunState.values()) {
sb.append(runState.getCode()).append(" ").append(counts[runState.ordinal()]).append(" ");
sb.append(runState.getCode()).append(":").append(counts[runState.ordinal()]).append(" ");
}
return sb.toString();
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 nosqlbench
* Copyright (c) 2022-2023 nosqlbench
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -27,13 +27,17 @@ public class RunStateImageTest {
public void testMaxStateImage() {
int[] counts = new int[RunState.values().length];
counts[RunState.Running.ordinal()]=3;
counts[RunState.Starting.ordinal()]=2;
RunStateImage image = new RunStateImage(counts, false);
assertThat(image.is(RunState.Running)).isTrue();
assertThat(image.is(RunState.Starting)).isTrue();
assertThat(image.isTimeout()).isFalse();
assertThat(image.is(RunState.Errored)).isFalse();
assertThat(image.isOnly(RunState.Running)).isTrue();
assertThat(image.isNoneOther(RunState.Starting, RunState.Running)).isTrue();
RunState maxState = image.getMaxState();
assertThat(maxState).isEqualTo(RunState.values()[2]);
assertThat(maxState).isEqualTo(RunState.values()[RunState.Running.ordinal()]);
RunState minState = image.getMinState();
assertThat(minState).isEqualTo(RunState.values()[RunState.Starting.ordinal()]);
}
}

View File

@ -1,8 +1,8 @@
Running Activities and Scenarios via CLI
========================================
PROG always runs a scenario script. However, there are multiple ways to tell
PROG what that script should be.
${PROG} always runs a scenario script. However, there are multiple ways to tell
${PROG} what that script should be.
Any argument in name=value format serves as a parameter to the
script or activity that precedes it.
@ -10,18 +10,18 @@ script or activity that precedes it.
To create a scenario script that simply runs a single activity to completion,
use this format:
~~~
PROG activity <param>=<value> [...]
${PROG} activity <param>=<value> [...]
~~~
To create a scenario script that runs multiple activities concurrently,
simply add more activities to the list:
~~~
PROG activity <param>=<value> [...] activity <param>=<value> [...]
${PROG} activity <param>=<value> [...] activity <param>=<value> [...]
~~~
To execute a scenario script directly, simply use the format:
~~~
PROG script <scriptname> [param=value [...]]
${PROG} script <scriptname> [param=value [...]]
~~~
Time & Size Units
@ -55,19 +55,19 @@ so parameters may be dropped into scripts ad-hoc.
By using the option --session-name <name>, you can name the session logfile
that will be (over)written with execution details.
~~~
PROG --session-name testsession42
${PROG} --session-name testsession42
~~~
## Metric Name
If you need to see what metrics are available for a particular activity type,
you can ask PROG to instantiate an activity of that type and discover the
you can ask ${PROG} to instantiate an activity of that type and discover the
metrics, dumping out a list. The following form of the command shows you how
to make a list that you can copy metric names from for scripting. If you provide
an example activity alias that matches one of your scripts, you can use it exactly
as it appears.
~~~
PROG --list-metrics driver=diag alias=anexample
${PROG} --list-metrics driver=diag alias=anexample
~~~
This will dump a list of metric names in the shortened format that is most suitable
for scenario script development. This format is required for the --list-metrics

View File

@ -1,4 +1,4 @@
### Command-Line Options ###
# Command-Line Options
Help ( You're looking at it. )
@ -8,27 +8,31 @@ Short options, like '-v' represent simple options, like verbosity. Using multipl
level of the option, like '-vvv'.
Long options, like '--help' are top-level options that may only be used once. These modify general
behavior, or allow you to get more details on how to use PROG.
behavior, or allow you to get more details on how to use ${PROG}.
All other options are either commands, or named arguments to commands. Any single word without
dashes is a command that will be converted into script form. Any option that includes an equals sign
is a named argument to the previous command. The following example is a commandline with a command *
start*, and two named arguments to that command.
PROG start driver=diag alias=example
${PROG} start driver=diag alias=example
### Discovery options ###
## Discovery options
These options help you learn more about running PROG, and about the plugins that are present in your
These options help you learn more about running ${PROG}, and about the plugins that are present in your
particular version.
Show version, long form, with artifact coordinates.
--version
Get a list of additional help topics that have more detailed documentation:
PROG help topics
${PROG} help topics
Provide specific help for the named activity type:
PROG help <activity type>
${PROG} help <activity type>
List the available drivers:
@ -50,9 +54,9 @@ Provide the metrics that are available for scripting
--list-metrics <activity type> [ <activity name> ]
### Execution Options ###
## Execution Options
This is how you actually tell PROG what scenario to run. Each of these commands appends script logic
This is how you actually tell ${PROG} what scenario to run. Each of these commands appends script logic
to the scenario that will be executed. These are considered as commands, can occur in any order and
quantity. The only rule is that arguments in the arg=value form will apply to the preceding script
or activity.
@ -65,9 +69,7 @@ Add the named activity to the scenario, interpolating named parameters
activity [arg=value]...
### General options ###
These options modify how the scenario is run.
## Logging options
Specify a directory for scenario log files:
@ -111,12 +113,38 @@ Specify the logging pattern for logfile only:
# ANSI variants are auto promoted for console if --ansi=enable
# ANSI variants are auto demoted for logfile in any case
## Console Options
Increase console logging levels: (Default console logging level is *warning*)
-v (info)
-vv (debug)
-vvv (trace)
--progress console:1m (disables itself if -v options are used)
These levels affect *only* the console output level. Other logging level parameters affect logging
to the scenario log, stored by default in logs/...
Explicitly enable or disable ANSI logging support:
(ANSI support is enabled if the TERM environment variable is defined)
--ansi=enabled
--ansi=disabled
Adjust the progress reporting interval:
--progress console:1m
or
--progress logonly:5m
NOTE: The progress indicator on console is provided by default unless logging levels are turned up
or there is a script invocation on the command line.
## Metrics options
Specify a directory and enable CSV reporting of metrics:
--report-csv-to <dirname>
@ -158,17 +186,6 @@ Each activity can also override this value with the hdr_digits parameter. Be awa
increase in this number multiples the amount of detail tracked on the client by 10x, so use
caution.
Adjust the progress reporting interval:
--progress console:1m
or
--progress logonly:5m
NOTE: The progress indicator on console is provided by default unless logging levels are turned up
or there is a script invocation on the command line.
If you want to add in classic time decaying histogram metrics for your histograms and timers, you
may do so with this option:
@ -191,22 +208,6 @@ automatically. It also imports a base dashboard for nosqlbench and configures gr
export to share with a central DataStax grafana instance (grafana can be found on localhost:3000
with the default credentials admin/admin).
### Console Options ###
Increase console logging levels: (Default console logging level is *warning*)
-v (info)
-vv (debug)
-vvv (trace)
--progress console:1m (disables itself if -v options are used)
These levels affect *only* the console output level. Other logging level parameters affect logging
to the scenario log, stored by default in logs/...
Show version, long form, with artifact coordinates.
--version
### Summary Reporting

View File

@ -79,6 +79,8 @@ public class ActivityExecutor implements ActivityController, ParameterMap.Listen
// TODO: Doc how uninitialized activities do not propagate parameter map changes and how
// TODO: this is different from preventing modification to uninitialized activities
// TODO: Determine whether this should really be synchronized
/**
* Simply stop the motors
*/
@ -183,7 +185,8 @@ public class ActivityExecutor implements ActivityController, ParameterMap.Listen
/**
* Shutdown the activity executor, with a grace period for the motor threads.
*
* @param initialMillisToWait milliseconds to wait after graceful shutdownActivity request, before forcing
* @param initialMillisToWait
* milliseconds to wait after graceful shutdownActivity request, before forcing
* everything to stop
*/
public synchronized void forceStopScenarioAndThrow(int initialMillisToWait, boolean rethrow) {
@ -234,7 +237,8 @@ public class ActivityExecutor implements ActivityController, ParameterMap.Listen
/**
* Stop extra motors, start missing motors
*
* @param activityDef the activityDef for this activity instance
* @param activityDef
* the activityDef for this activity instance
*/
private void adjustMotorCountToThreadParam(ActivityDef activityDef) {
logger.trace(() -> ">-pre-adjust->" + getSlotStatus());
@ -276,7 +280,7 @@ public class ActivityExecutor implements ActivityController, ParameterMap.Listen
}
}
private void alignMotorStateToIntendedActivityState() {
private synchronized void alignMotorStateToIntendedActivityState() {
RunState intended = activity.getRunState();
logger.trace(() -> "ADJUSTING to INTENDED " + intended);
switch (intended) {
@ -311,26 +315,28 @@ public class ActivityExecutor implements ActivityController, ParameterMap.Listen
private void awaitAlignmentOfMotorStateToActivityState() {
logger.debug(() -> "awaiting state alignment from " + activity.getRunState());
RunStateImage states = null;
switch (activity.getRunState()) {
case Starting:
case Running:
tally.awaitNoneOther(RunState.Running, RunState.Finished);
states = tally.awaitNoneOther(RunState.Running, RunState.Finished);
break;
case Errored:
case Stopping:
case Stopped:
tally.awaitNoneOther(RunState.Stopped, RunState.Finished, RunState.Errored);
states = tally.awaitNoneOther(RunState.Stopped, RunState.Finished, RunState.Errored);
break;
case Uninitialized:
break;
case Finished:
tally.awaitNoneOther(RunState.Finished);
states = tally.awaitNoneOther(RunState.Finished);
break;
default:
throw new RuntimeException("Unmatched run state:" + activity.getRunState());
}
logger.debug("activity and threads are aligned to state " + activity.getRunState() + " for " + this.getActivity().getAlias());
RunState previousState = activity.getRunState();
activity.setRunState(states.getMaxState());
logger.debug("activity and threads are aligned to state " + previousState + " for " + this.getActivity().getAlias() + ", and advanced to " + activity.getRunState());
}
@ -391,12 +397,16 @@ public class ActivityExecutor implements ActivityController, ParameterMap.Listen
// instantiate and configure fixtures that need to be present
// before threads start running such as metrics instruments
activity.initActivity();
startMotorExecutorService();
startRunningActivityThreads();
awaitMotorsAtLeastRunning();
logger.debug("STARTED " + activityDef.getAlias());
awaitActivityCompletion();
activity.shutdownActivity();
activity.closeAutoCloseables();
} catch (Exception e) {
this.exception = e;
} finally {
activity.shutdownActivity();
activity.closeAutoCloseables();
}
ExecutionResult result = new ExecutionResult(startedAt, stoppedAt, "", exception);
return result;
@ -420,7 +430,10 @@ public class ActivityExecutor implements ActivityController, ParameterMap.Listen
}
public synchronized void startActivity() {
// we need an executor service to run motor threads on
RunStateImage startable = tally.awaitNoneOther(1000L, RunState.Uninitialized, RunState.Stopped);
if (startable.isTimeout()) {
throw new RuntimeException("Unable to start activity '" + getActivity().getAlias() + "' which is in state " + startable);
}
startMotorExecutorService();
startRunningActivityThreads();
awaitMotorsAtLeastRunning();

View File

@ -86,8 +86,6 @@ public class ScenarioController {
Future<ExecutionResult> startedActivity = activitiesExecutor.submit(executor);
ActivityRuntimeInfo activityRuntimeInfo = new ActivityRuntimeInfo(activity, startedActivity, executor);
this.activityInfoMap.put(activity.getAlias(), activityRuntimeInfo);
executor.startActivity();
scenariologger.debug("STARTED " + activityDef.getAlias());
}
return this.activityInfoMap.get(activityDef.getAlias());
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 nosqlbench
* Copyright (c) 2022-2023 nosqlbench
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -47,7 +47,7 @@ class ActivityExecutorTest {
@Test
synchronized void testRestart() {
ActivityDef activityDef = ActivityDef.parseActivityDef("driver=diag;alias=test;cycles=1000;op=initdelay:initdelay=5000;");
ActivityDef activityDef = ActivityDef.parseActivityDef("driver=diag;alias=test-restart;cycles=1000;cyclerate=1;op=initdelay:initdelay=5000;");
new ActivityTypeLoader().load(activityDef);
final Activity activity = new DelayedInitActivity(activityDef);
@ -67,23 +67,23 @@ class ActivityExecutorTest {
try {
activityDef.setThreads(1);
activityExecutor.startActivity();
Thread.sleep(500L);
activityExecutor.stopActivity();
activityExecutor.startActivity();
activityExecutor.startActivity();
activityExecutor.stopActivity();
future.get();
Thread.sleep(500L);
} catch (Exception e) {
throw new RuntimeException(e);
}
executor.shutdown();
assertThat(inputDispenser.getInput(10).getInputSegment(3)).isNull();
assertThat(inputDispenser.getInput(10).getInputSegment(3)).isNotNull();
}
@Test
synchronized void testDelayedStartSanity() {
final ActivityDef activityDef = ActivityDef.parseActivityDef("driver=diag;alias=test;cycles=1000;initdelay=5000;");
final ActivityDef activityDef = ActivityDef.parseActivityDef("driver=diag;alias=test-delayed-start;cycles=1000;initdelay=2000;");
new ActivityTypeLoader().load(activityDef);
final Activity activity = new DelayedInitActivity(activityDef);
@ -119,7 +119,7 @@ class ActivityExecutorTest {
@Test
synchronized void testNewActivityExecutor() {
ActivityDef activityDef = ActivityDef.parseActivityDef("driver=diag;alias=test;cycles=1000;initdelay=5000;");
ActivityDef activityDef = ActivityDef.parseActivityDef("driver=diag;alias=test-dynamic-params;cycles=1000;initdelay=5000;");
new ActivityTypeLoader().load(activityDef);
getActivityMotorFactory(motorActionDelay(999), new AtomicInput(activityDef));
@ -140,7 +140,7 @@ class ActivityExecutorTest {
activityDef.setThreads(5);
activityExecutor.startActivity();
int[] speeds = new int[]{1, 2000, 5, 2000, 2, 2000};
int[] speeds = new int[]{1, 50, 5, 50, 2, 50};
for (int offset = 0; offset < speeds.length; offset += 2) {
int threadTarget = speeds[offset];
int threadTime = speeds[offset + 1];
@ -158,7 +158,7 @@ class ActivityExecutorTest {
// Used for slowing the roll due to state transitions in test.
try {
activityExecutor.stopActivity();
Thread.sleep(2000L);
// Thread.sleep(2000L);
} catch (Exception e) {
fail("Not expecting exception", e);
}

View File

@ -40,6 +40,7 @@
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<PROG>nb5</PROG>
</properties>
<name>${project.artifactId}</name>