From 86c4dfd9669e168d7203339fbc59682bd15de7aa Mon Sep 17 00:00:00 2001 From: Jonathan Shook Date: Wed, 30 Nov 2022 11:17:10 -0600 Subject: [PATCH 01/19] partial fixes for nosqlbench-797 Race condition between exceptional activity shutdown and normal scenario shutdown. --- ...sync_activity-Lifecycle_of_an_activity.svg | 236 ++++++++++++++++++ devdocs/concurrency/async_activity.puml | 105 ++++++++ ...-Lifecycle_of_a_single_scenario_call__.svg | 160 ++++++++++++ devdocs/concurrency/async_scenario.puml | 67 +++++ ...async_scenarios-Lifecycle_of_Scenarios.svg | 150 +++++++++++ devdocs/concurrency/async_scenarios.puml | 62 +++++ .../engine/api/activityapi/core/RunState.java | 98 ++++---- .../java/io/nosqlbench/engine/cli/NBCLI.java | 6 +- .../lifecycle/ActivityExceptionHandler.java | 4 +- .../core/lifecycle/ActivityFinisher.java | 4 +- .../engine/core/lifecycle/ActivityStatus.java | 20 ++ ...cutor.java => ActivityThreadsManager.java} | 180 ++++++------- ...arioResult.java => ExecMetricsResult.java} | 67 +---- .../engine/core/lifecycle/ExecResult.java | 68 +++++ .../core/lifecycle/ScenarioController.java | 67 ++--- .../core/lifecycle/ScenariosResults.java | 21 +- .../core/lifecycle/StartedActivityInfo.java | 29 +++ .../engine/core/script/Scenario.java | 61 +++-- .../engine/core/script/ScenariosExecutor.java | 24 +- ...t.java => ActivityThreadsManagerTest.java} | 12 +- .../resources/ScenarioExecutorEndpoint.java | 6 +- .../engine/rest/transfertypes/ResultView.java | 6 +- .../nbr/examples/ScriptExampleTests.java | 50 ++-- .../examples/SpeedCheckIntegrationTests.java | 6 +- .../testing/ExitStatusIntegrationTests.java | 28 +-- 25 files changed, 1184 insertions(+), 353 deletions(-) create mode 100644 devdocs/concurrency/async_activity-Lifecycle_of_an_activity.svg create mode 100644 devdocs/concurrency/async_activity.puml create mode 100644 devdocs/concurrency/async_scenario-Lifecycle_of_a_single_scenario_call__.svg create mode 100644 devdocs/concurrency/async_scenario.puml create mode 100644 devdocs/concurrency/async_scenarios-Lifecycle_of_Scenarios.svg create mode 100644 devdocs/concurrency/async_scenarios.puml create mode 100644 engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ActivityStatus.java rename engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/{ActivityExecutor.java => ActivityThreadsManager.java} (77%) rename engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/{ScenarioResult.java => ExecMetricsResult.java} (68%) create mode 100644 engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ExecResult.java create mode 100644 engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/StartedActivityInfo.java rename engine-core/src/test/java/io/nosqlbench/engine/core/{ActivityExecutorTest.java => ActivityThreadsManagerTest.java} (94%) diff --git a/devdocs/concurrency/async_activity-Lifecycle_of_an_activity.svg b/devdocs/concurrency/async_activity-Lifecycle_of_an_activity.svg new file mode 100644 index 000000000..e159d5c6d --- /dev/null +++ b/devdocs/concurrency/async_activity-Lifecycle_of_an_activity.svg @@ -0,0 +1,236 @@ + + +Lifecycle of an activitycallercallerActivityExecutorActivityExceptionHandlerActivityThreadFactoryExecutorServiceAnnotatorAnnotatorActivityActivitythreadstartup sequencecreateActivityExecutorcreateActivityExceptionHandlercreate(\w Exception Handler)ActivityThreadFactory<injectedvia ctor>create(\w Thread Factory)ExecutorService<injectedvia ctor>startActivity()Annotate Activity StartinitActivity()align threadcount as explained belowdynamic threadcount updatethreads can be changed dynamicallyapply paramsalign motor countstop extra motors<start missing motors>for each new thread/motorexecute(<motor>)get()createthread<thread>run()At this point, themotor thread starts runningthe defined activity's actionover cyclesawait thread state updateshutdown sequence [after startup]stopActivity()request stop motorsawait all stopshutdownActivity()Annotate Activity Finishon exception in motor threadcatch(<thrown exception>)notifyException(<thread>,<throwable>)save exceptionforceStopActivity()shutdown();if needed[after timeout]]shutdownNow();shutdownActivity();closeAutoCloseables();actionthreadterminates diff --git a/devdocs/concurrency/async_activity.puml b/devdocs/concurrency/async_activity.puml new file mode 100644 index 000000000..cb276a443 --- /dev/null +++ b/devdocs/concurrency/async_activity.puml @@ -0,0 +1,105 @@ +@startuml +'https://plantuml.com/sequence-diagram +title: Lifecycle of an activity + +control caller as caller +control ActivityExecutor as ae +control "Activity\nException\nHandler" as aeh +control "Activity\nThread\nFactory" as atf +control ExecutorService as aes +control Annotator as ann +control Activity as activity + +== startup sequence == +caller -> ae**: create + ae -> aeh**: create + ae -> atf**: create(\w Exception Handler) + aeh -> atf: + ae -> aes**: create(\w Thread Factory) + atf -> aes: + +caller -> ae: startActivity() +activate ae + ae -> ann: Annotate Activity Start + + ae -> activity: initActivity() + activate activity + ae <- activity + deactivate activity + + note over ae,aes: align threadcount as explained below + +caller <- ae +deactivate ae + +== dynamic threadcount update == +note over ae, aes: threads can be changed dynamically + +caller -> ae: apply params +activate ae + ae->ae: align motor count + ae->aes: stop extra motors + ae->aes: + group for each new thread/motor + ae -> aes: execute() + activate aes + aes -> atf: get() + atf -> thread**: create + activate atf + aes <- atf: + deactivate atf + aes --> thread: run() + note over ann, thread: At this point, the\nmotor thread starts running\nthe defined activity's action\nover cycles + ae->ae: await thread state update + + ae<-aes: + deactivate aes + end group +caller <- ae +deactivate ae + +== shutdown sequence [after startup] == + +caller -> ae: stopActivity() +activate ae + + ae -> ae: request stop motors + ae -> ae: await all stop + + ae -> activity: shutdownActivity() + activate activity + ae <- activity + deactivate activity + + ae -> ann: Annotate Activity Finish + +caller <- ae +deactivate ae + +== on exception in motor thread == +thread -> aeh: catch() +aeh -> ae: notifyException\n(,) +activate ae + ae -> ae: save exception + ae -> ae: forceStopActivity() + ae -> aes: shutdown(); + activate aes + ae <- aes: + deactivate aes + + group if needed [after timeout]] + ae -> aes: shutdownNow(); + activate aes + ae <- aes + deactivate aes + end group + + ae -> activity: shutdownActivity(); + ae -> activity: closeAutoCloseables(); + + note over thread: action\nthread\nterminates + destroy thread +deactivate ae + + +@enduml diff --git a/devdocs/concurrency/async_scenario-Lifecycle_of_a_single_scenario_call__.svg b/devdocs/concurrency/async_scenario-Lifecycle_of_a_single_scenario_call__.svg new file mode 100644 index 000000000..3bb291402 --- /dev/null +++ b/devdocs/concurrency/async_scenario-Lifecycle_of_a_single_scenario_call__.svg @@ -0,0 +1,160 @@ + + +Lifecycle of a single scenario.call()callercallerScenarioScenarioControllerScriptingEngineActivityExecutorJavaRuntimeJavaRuntimeShutdownHookAnnotationsAnnotationscreateScenariocall()createShutdownHookregister(ShutdownHook)Annotate Scenario StartcreateScenarioControllercreateScriptingEnginerun(script)async calls[javacript+Java]scenario.(*)activities.(*)metrics.(*)params.(*)start(<activity>)createActivityExecutorstartActivity()resultawaitCompletion()for each activityawaitCompletion()unregister(ShutdownHook)run()Annotate Scenario FinishScenarioResulton exception during call()run()Annotate Scenario Finish diff --git a/devdocs/concurrency/async_scenario.puml b/devdocs/concurrency/async_scenario.puml new file mode 100644 index 000000000..eee38edf1 --- /dev/null +++ b/devdocs/concurrency/async_scenario.puml @@ -0,0 +1,67 @@ +@startuml +'https://plantuml.com/sequence-diagram + +title Lifecycle of a single scenario.call() +control "caller" as c +control "Scenario" as s +control "Scenario\nController" as sc +control "Scripting\nEngine" as engine +control "Activity\nExecutor" as ae +control "Java\nRuntime" as jrt +control "Shutdown\nHook" as sh +control "Annotations" as ann + +c -> s**: create + +c -> s: call() +activate s + + s -> sh**: create + s -> jrt: register(ShutdownHook) + s -> ann: Annotate Scenario Start + + s -> sc**: create + s -> engine**: create + + s -> engine: run(script) + activate engine + group async calls [javacript+Java] + engine <--> sc: scenario.(*) + engine <--> sc: activities.(*) + engine <--> sc: metrics.(*) + engine <--> sc: params.(*) + engine -> sc: start() + activate sc + sc -> ae**: create + sc -> ae: startActivity() + + deactivate sc + end group + s <- engine: result + deactivate engine + + s -> sc: awaitCompletion() + activate sc + group for each activity + sc -> ae: awaitCompletion() + activate ae + sc <- ae + deactivate ae + end group + + s <- sc + deactivate sc + + s -> jrt: unregister(ShutdownHook) + s -> sh: run() + sh -> ann: Annotate Scenario Finish + +c <- s: Scenario\nResult +deactivate s + +== on exception during call() == + jrt -> sh: run() + sh -> ann: Annotate Scenario Finish + + +@enduml diff --git a/devdocs/concurrency/async_scenarios-Lifecycle_of_Scenarios.svg b/devdocs/concurrency/async_scenarios-Lifecycle_of_Scenarios.svg new file mode 100644 index 000000000..b482fb579 --- /dev/null +++ b/devdocs/concurrency/async_scenarios-Lifecycle_of_Scenarios.svg @@ -0,0 +1,150 @@ + + +Lifecycle of ScenariosNBCLINBCLIScenarioScenarioControllerScenariosExecutorExceptionHandlerThreadFactoryExecutorServicefuturethreadcreateScenariosExecutorcreateExceptionHandlercreate ThreadFactory(w/ ExceptionHandler)ThreadFactorycreate ExecutorService(w/ ThreadFactory)ExecutorServicecreateScenariocreateScenarioControllerexecute(Scenario)submit(<Callable> Scenario)createfuture<Future<ScenarioResult>>[async] on thread from thread factoryget()createthread<thread>run taskcall()ScenarioResultresult[async] on NBCLI threadawaitAllResults();shutdownloop[timeout]awaitTermination(timeout)loop[each future]get()ScenarioResult<ScenariosResults> diff --git a/devdocs/concurrency/async_scenarios.puml b/devdocs/concurrency/async_scenarios.puml new file mode 100644 index 000000000..89410edd7 --- /dev/null +++ b/devdocs/concurrency/async_scenarios.puml @@ -0,0 +1,62 @@ +@startuml +'https://plantuml.com/sequence-diagram + +title Lifecycle of Scenarios + +control "NBCLI" as nbcli +control "Scenario" as s +control "Scenario\nController" as sc +control "Scenarios\nExecutor" as se +control "Exception\nHandler" as seh +control "Thread\nFactory" as stf +control "Executor\nService" as ses + +nbcli -> se** : create + se -> seh** : create + se -> stf** : create ThreadFactory\n(w/ ExceptionHandler) + se -> ses** : create ExecutorService\n(w/ ThreadFactory) + +nbcli -> s** : create + s -> sc** : create +nbcli --> se : execute(Scenario) +se --> ses: submit( Scenario) +activate ses + ses -> future**: create +se <-- ses: > +deactivate ses + +== [async] on thread from thread factory == +ses -> stf: get() + stf -> thread**: create +ses <- stf: +ses -> thread: run task +activate thread +thread -> s: call() +activate s +thread <- s: ScenarioResult +deactivate s +thread -> future: result +deactivate thread + +== [async] on NBCLI thread == + +nbcli -> se: awaitAllResults(); +activate se + se -> ses: shutdown + loop timeout + se -> ses: awaitTermination(timeout) + activate ses + se <- ses + deactivate ses + end loop + loop each future + se -> future: get() + activate future + se <- future: ScenarioResult + deactivate future + end loop + +nbcli <- se: +deactivate se + +@enduml diff --git a/engine-api/src/main/java/io/nosqlbench/engine/api/activityapi/core/RunState.java b/engine-api/src/main/java/io/nosqlbench/engine/api/activityapi/core/RunState.java index b78b8a247..15ca92a96 100644 --- a/engine-api/src/main/java/io/nosqlbench/engine/api/activityapi/core/RunState.java +++ b/engine-api/src/main/java/io/nosqlbench/engine/api/activityapi/core/RunState.java @@ -18,18 +18,36 @@ package io.nosqlbench.engine.api.activityapi.core; public enum RunState { - // Initial state after creation of this control + + /** + * Initial state after creation of this control + */ Uninitialized("i⌀"), - // This thread has been queued to run, but hasn't signaled yet that it is full started - // This must be set by the executor before executing the slot runnable + + /** + * This thread has been queued to run, but hasn't signaled yet that it is full started + * This must be set by the executor before executing the slot runnable + */ Starting("s⏫"), - // This thread is running. This should only be set by the controlled thread + + /** + * This thread is running. This should only be set by the controlled thread + */ Running("R\u23F5"), - // This thread has completed all of its activity, and will do no further work without new input + + /** + * This thread has completed all of its activity, and will do no further work without new input + */ Finished("F⏯"), - // The thread has been requested to stop. This says nothing of the internal state. + + /** + * The thread has been requested to stop. This says nothing of the internal state. + */ Stopping("s⏬"), - // The thread has stopped. This should only be set by the controlled thread + + /** + * The thread has stopped. This should only be set by the controlled thread + */ Stopped("_\u23F9"); private final String runcode; @@ -42,53 +60,25 @@ public enum RunState { return this.runcode; } - public boolean canTransitionTo(RunState to) { - switch (this) { - default: - return false; - case Uninitialized: // A motor was just created. This is its initial state. - case Stopped: - switch (to) { - case Starting: // a motor has been reserved for an execution command - return true; - default: - return false; - } - case Starting: - switch (to) { - case Running: // a motor has indicated that is in the run() method - case Finished: // a motor has exhausted its input, and has declined to go into started mode - return true; - default: - return false; - } - case Running: - switch (to) { - case Stopping: // A request was made to stop the motor before it finished - case Finished: // A motor has exhausted its input, and is finished with its work - return true; - default: - return false; - } - case Stopping: - switch (to) { - case Stopped: // A motor was stopped by request before exhausting input - return true; - default: - return false; - }// A motor was restarted after being stopped - case Finished: - switch (to) { - case Running: // A motor was restarted? - return true; - // not useful as of yet. - // Perhaps this will be allowed via explicit reset of input stream. - // If the input isn't reset, then trying to start a finished motor - // will cause it to short-circuit back to Finished state. - default: - return false; - } - } + /** + * @param target The target state + * @return true if the current state is allowed to transition to the target state + */ + public boolean canTransitionTo(RunState target) { + return switch (this) { + default -> false; // A motor was just created. This is its initial state. + case Uninitialized, Stopped -> (target == Starting); + case Starting -> switch (target) { // a motor has indicated that is in the run() method + case Running, Finished -> true;// a motor has exhausted its input, and has declined to go into started mode + default -> false; + }; + case Running -> switch (target) { // A request was made to stop the motor before it finished + case Stopping, Finished -> true;// A motor has exhausted its input, and is finished with its work + default -> false; + }; + case Stopping -> (target == Stopped); // A motor was stopped by request before exhausting input + case Finished -> (target == Running); // A motor was restarted? + }; } diff --git a/engine-cli/src/main/java/io/nosqlbench/engine/cli/NBCLI.java b/engine-cli/src/main/java/io/nosqlbench/engine/cli/NBCLI.java index 1111d4507..08df6ddc6 100644 --- a/engine-cli/src/main/java/io/nosqlbench/engine/cli/NBCLI.java +++ b/engine-cli/src/main/java/io/nosqlbench/engine/cli/NBCLI.java @@ -418,7 +418,7 @@ public class NBCLI implements Function { // intentionally not shown for warn-only logger.info("console logging level is " + options.getConsoleLogLevel()); - ScenariosExecutor executor = new ScenariosExecutor("executor-" + sessionName, 1); + ScenariosExecutor scenariosExecutor = new ScenariosExecutor("executor-" + sessionName, 1); if (options.getConsoleLogLevel().isGreaterOrEqualTo(NBLogLevel.WARN)) { options.setWantsStackTraces(true); logger.debug("enabling stack traces since log level is " + options.getConsoleLogLevel()); @@ -466,7 +466,7 @@ public class NBCLI implements Function { scriptParams.putAll(buffer.getCombinedParams()); scenario.addScenarioScriptParams(scriptParams); - executor.execute(scenario); + scenariosExecutor.execute(scenario); // while (true) { // Optional pendingResult = executor.getPendingResult(scenario.getScenarioName()); @@ -476,7 +476,7 @@ public class NBCLI implements Function { // LockSupport.parkNanos(100000000L); // } - ScenariosResults scenariosResults = executor.awaitAllResults(); + ScenariosResults scenariosResults = scenariosExecutor.awaitAllResults(); logger.debug("Total of " + scenariosResults.getSize() + " result object returned from ScenariosExecutor"); ActivityMetrics.closeMetrics(options.wantsEnableChart()); diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ActivityExceptionHandler.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ActivityExceptionHandler.java index 5beb7643a..b088829f3 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ActivityExceptionHandler.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ActivityExceptionHandler.java @@ -23,9 +23,9 @@ public class ActivityExceptionHandler implements Thread.UncaughtExceptionHandler private static final Logger logger = LogManager.getLogger(ActivityExceptionHandler.class); - private final ActivityExecutor executor; + private final ActivityThreadsManager executor; - public ActivityExceptionHandler(ActivityExecutor executor) { + public ActivityExceptionHandler(ActivityThreadsManager executor) { this.executor = executor; logger.debug(() -> "Activity exception handler starting up for executor '" + executor + "'"); } diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ActivityFinisher.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ActivityFinisher.java index 845f7c272..5b8588d80 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ActivityFinisher.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ActivityFinisher.java @@ -22,11 +22,11 @@ import org.apache.logging.log4j.Logger; public class ActivityFinisher extends Thread { private final static Logger logger = LogManager.getLogger(ActivityFinisher.class); - private final ActivityExecutor executor; + private final ActivityThreadsManager executor; private final int timeout; private boolean result; - public ActivityFinisher(ActivityExecutor executor, int timeout) { + public ActivityFinisher(ActivityThreadsManager executor, int timeout) { super(executor.getActivityDef().getAlias() + "_finisher"); this.executor = executor; this.timeout = timeout; diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ActivityStatus.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ActivityStatus.java new file mode 100644 index 000000000..d77927fbc --- /dev/null +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ActivityStatus.java @@ -0,0 +1,20 @@ +/* + * 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; + +public class ActivityStatus { +} diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ActivityExecutor.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ActivityThreadsManager.java similarity index 77% rename from engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ActivityExecutor.java rename to engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ActivityThreadsManager.java index 2ca5bfd71..afc53094e 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ActivityExecutor.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ActivityThreadsManager.java @@ -26,11 +26,10 @@ import io.nosqlbench.engine.core.annotation.Annotators; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import java.util.*; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.SynchronousQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.*; import java.util.stream.Collectors; /** @@ -49,9 +48,9 @@ import java.util.stream.Collectors; * */ -public class ActivityExecutor implements ActivityController, ParameterMap.Listener, ProgressCapable { +public class ActivityThreadsManager implements ActivityController, ParameterMap.Listener, ProgressCapable, Callable { - private static final Logger logger = LogManager.getLogger(ActivityExecutor.class); + private static final Logger logger = LogManager.getLogger(ActivityThreadsManager.class); private static final Logger activitylogger = LogManager.getLogger("ACTIVITY"); private final List> motors = new ArrayList<>(); @@ -68,25 +67,20 @@ public class ActivityExecutor implements ActivityController, ParameterMap.Listen // private RunState intendedState = RunState.Uninitialized; - public ActivityExecutor(Activity activity, String sessionId) { + public ActivityThreadsManager(Activity activity, String sessionId) { this.activity = activity; this.activityDef = activity.getActivityDef(); executorService = new ThreadPoolExecutor( - 0, Integer.MAX_VALUE, - 0L, TimeUnit.SECONDS, - new SynchronousQueue<>(), - new IndexedThreadFactory(activity.getAlias(), new ActivityExceptionHandler(this)) + 0, Integer.MAX_VALUE, + 0L, TimeUnit.SECONDS, + new SynchronousQueue<>(), + new IndexedThreadFactory(activity.getAlias(), new ActivityExceptionHandler(this)) ); activity.getActivityDef().getParams().addListener(this); activity.setActivityController(this); this.sessionId = sessionId; } - public void setSessionId(String sessionId) { - this.sessionId = sessionId; - } - - // TODO: Doc how uninitialized activities do not propagate parameter map changes and how // TODO: this is different from preventing modification to uninitialized activities @@ -101,14 +95,14 @@ public class ActivityExecutor implements ActivityController, ParameterMap.Listen public synchronized void startActivity() { logger.info("starting activity " + activity.getAlias() + " for cycles " + activity.getCycleSummary()); Annotators.recordAnnotation(Annotation.newBuilder() - .session(sessionId) - .now() - .layer(Layer.Activity) - .label("alias", getActivityDef().getAlias()) - .label("driver", getActivityDef().getActivityType()) - .label("workload", getActivityDef().getParams().getOptionalString("workload").orElse("none")) - .detail("params", getActivityDef().toString()) - .build() + .session(sessionId) + .now() + .layer(Layer.Activity) + .label("alias", getActivityDef().getAlias()) + .label("driver", getActivityDef().getActivityType()) + .label("workload", getActivityDef().getParams().getOptionalString("workload").orElse("none")) + .detail("params", getActivityDef().toString()) + .build() ); activitylogger.debug("START/before alias=(" + activity.getAlias() + ")"); @@ -130,7 +124,7 @@ public class ActivityExecutor implements ActivityController, ParameterMap.Listen /** * Simply stop the motors */ - public synchronized void stopActivity() { + private synchronized void stopActivity() { activitylogger.debug("STOP/before alias=(" + activity.getAlias() + ")"); activity.setRunState(RunState.Stopping); @@ -142,19 +136,20 @@ public class ActivityExecutor implements ActivityController, ParameterMap.Listen activity.setRunState(RunState.Stopped); logger.info("stopped: " + this.getActivityDef().getAlias() + " with " + motors.size() + " slots"); activitylogger.debug("STOP/after alias=(" + activity.getAlias() + ")"); + Annotators.recordAnnotation(Annotation.newBuilder() - .session(sessionId) - .interval(this.startedAt, this.stoppedAt) - .layer(Layer.Activity) - .label("alias", getActivityDef().getAlias()) - .label("driver", getActivityDef().getActivityType()) - .label("workload", getActivityDef().getParams().getOptionalString("workload").orElse("none")) - .detail("params", getActivityDef().toString()) - .build() + .session(sessionId) + .interval(this.startedAt, this.stoppedAt) + .layer(Layer.Activity) + .label("alias", getActivityDef().getAlias()) + .label("driver", getActivityDef().getActivityType()) + .label("workload", getActivityDef().getParams().getOptionalString("workload").orElse("none")) + .detail("params", getActivityDef().toString()) + .build() ); } - public synchronized RuntimeException forceStopScenario(int initialMillisToWait) { + public RuntimeException forceStopActivity(int initialMillisToWait) { activitylogger.debug("FORCE STOP/before alias=(" + activity.getAlias() + ")"); activity.setRunState(RunState.Stopped); @@ -206,14 +201,14 @@ public class ActivityExecutor implements ActivityController, ParameterMap.Listen * @param initialMillisToWait milliseconds to wait after graceful shutdownActivity request, before forcing * everything to stop */ - public synchronized void forceStopScenarioAndThrow(int initialMillisToWait, boolean rethrow) { - RuntimeException exception = forceStopScenario(initialMillisToWait); + private synchronized void forceStopScenarioAndThrow(int initialMillisToWait, boolean rethrow) { + RuntimeException exception = forceStopActivity(initialMillisToWait); if (exception != null && rethrow) { throw exception; } } - public boolean finishAndShutdownExecutor(int secondsToWait) { + private boolean finishAndShutdownExecutor(int secondsToWait) { activitylogger.debug("REQUEST STOP/before alias=(" + activity.getAlias() + ")"); logger.debug("Stopping executor for " + activity.getAlias() + " when work completes."); @@ -245,6 +240,7 @@ public class ActivityExecutor implements ActivityController, ParameterMap.Listen if (stoppingException != null) { logger.trace(() -> "an exception caused the activity to stop:" + stoppingException.getMessage()); + logger.trace("Setting ERROR on activity executor: " + stoppingException.getMessage()); throw stoppingException; } @@ -270,10 +266,10 @@ public class ActivityExecutor implements ActivityController, ParameterMap.Listen adjustToActivityDef(activity.getActivityDef()); } motors.stream() - .filter(m -> (m instanceof ActivityDefObserver)) + .filter(m -> (m instanceof ActivityDefObserver)) // .filter(m -> m.getSlotStateTracker().getSlotState() != RunState.Uninitialized) // .filter(m -> m.getSlotStateTracker().getSlotState() != RunState.Starting) - .forEach(m -> ((ActivityDefObserver) m).onActivityDefUpdate(activityDef)); + .forEach(m -> ((ActivityDefObserver) m).onActivityDefUpdate(activityDef)); } } @@ -288,25 +284,25 @@ public class ActivityExecutor implements ActivityController, ParameterMap.Listen *

* TODO: move activity finisher thread to this class and remove separate implementation */ - public boolean awaitCompletion(int waitTime) { - logger.debug(()-> "awaiting completion of '" + this.getActivity().getAlias() + "'"); + private boolean awaitCompletion(int waitTime) { + logger.debug(() -> "awaiting completion of '" + this.getActivity().getAlias() + "'"); boolean finished = finishAndShutdownExecutor(waitTime); Annotators.recordAnnotation(Annotation.newBuilder() - .session(sessionId) - .interval(startedAt, this.stoppedAt) - .layer(Layer.Activity) - .label("alias", getActivityDef().getAlias()) - .label("driver", getActivityDef().getActivityType()) - .label("workload", getActivityDef().getParams().getOptionalString("workload").orElse("none")) - .detail("params", getActivityDef().toString()) - .build() + .session(sessionId) + .interval(startedAt, this.stoppedAt) + .layer(Layer.Activity) + .label("alias", getActivityDef().getAlias()) + .label("driver", getActivityDef().getActivityType()) + .label("workload", getActivityDef().getParams().getOptionalString("workload").orElse("none")) + .detail("params", getActivityDef().toString()) + .build() ); return finished; } - public boolean awaitFinish(int timeout) { + public boolean awaitFinishedOrStopped(int timeout) { activitylogger.debug("AWAIT-FINISH/before alias=(" + activity.getAlias() + ")"); boolean awaited = awaitAllRequiredMotorState(timeout, 50, RunState.Finished, RunState.Stopped); @@ -327,8 +323,8 @@ public class ActivityExecutor implements ActivityController, ParameterMap.Listen private String getSlotStatus() { return motors.stream() - .map(m -> m.getSlotStateTracker().getSlotState().getCode()) - .collect(Collectors.joining(",", "[", "]")); + .map(m -> m.getSlotStateTracker().getSlotState().getCode()) + .collect(Collectors.joining(",", "[", "]")); } /** @@ -371,18 +367,18 @@ public class ActivityExecutor implements ActivityController, ParameterMap.Listen case Running: case Starting: motors.stream() - .filter(m -> m.getSlotStateTracker().getSlotState() != RunState.Running) - .filter(m -> m.getSlotStateTracker().getSlotState() != RunState.Finished) - .filter(m -> m.getSlotStateTracker().getSlotState() != RunState.Starting) - .forEach(m -> { - m.getSlotStateTracker().enterState(RunState.Starting); - executorService.execute(m); - }); + .filter(m -> m.getSlotStateTracker().getSlotState() != RunState.Running) + .filter(m -> m.getSlotStateTracker().getSlotState() != RunState.Finished) + .filter(m -> m.getSlotStateTracker().getSlotState() != RunState.Starting) + .forEach(m -> { + m.getSlotStateTracker().enterState(RunState.Starting); + executorService.execute(m); + }); break; case Stopped: motors.stream() - .filter(m -> m.getSlotStateTracker().getSlotState() != RunState.Stopped) - .forEach(Motor::requestStop); + .filter(m -> m.getSlotStateTracker().getSlotState() != RunState.Stopped) + .forEach(Motor::requestStop); break; case Finished: case Stopping: @@ -421,31 +417,23 @@ public class ActivityExecutor implements ActivityController, ParameterMap.Listen * Await a thread (aka motor/slot) entering a specific SlotState * * @param m motor instance - * @param waitTime milliseco`nds to wait, total + * @param waitTime milliseconds to wait, total * @param pollTime polling interval between state checks * @param desiredRunStates any desired SlotState * @return true, if the desired SlotState was detected */ - private boolean awaitMotorState(Motor m, int waitTime, int pollTime, RunState... desiredRunStates) { + private boolean awaitMotorState(Motor m, int waitTime, int pollTime, RunState... desiredRunStates) { long startedAt = System.currentTimeMillis(); while (System.currentTimeMillis() < (startedAt + waitTime)) { - Map actualStates = new HashMap<>(); - for (RunState state : desiredRunStates) { - actualStates.compute(state, (k, v) -> (v == null ? 0 : v) + 1); - } for (RunState desiredRunState : desiredRunStates) { - actualStates.remove(desiredRunState); - } - logger.trace(() -> "state of remaining slots:" + actualStates); - if (actualStates.size() == 0) { - return true; - } else { - System.out.println("motor states:" + actualStates); - try { - Thread.sleep(pollTime); - } catch (InterruptedException ignored) { + if (desiredRunState == m.getSlotStateTracker().getSlotState()) { + return true; } } + try { + Thread.sleep(pollTime); + } catch (InterruptedException ignored) { + } } logger.trace(() -> activityDef.getAlias() + "/Motor[" + m.getSlotId() + "] is now in state " + m.getSlotStateTracker().getSlotState()); return false; @@ -461,7 +449,7 @@ public class ActivityExecutor implements ActivityController, ParameterMap.Listen awaited = awaitMotorState(motor, waitTime, pollTime, awaitingState); if (!awaited) { logger.trace(() -> "failed awaiting motor " + motor.getSlotId() + " for state in " + - Arrays.asList(awaitingState)); + Arrays.asList(awaitingState)); break; } } @@ -469,28 +457,6 @@ public class ActivityExecutor implements ActivityController, ParameterMap.Listen return awaited; } - - private boolean awaitAnyRequiredMotorState(int waitTime, int pollTime, RunState... awaitingState) { - long startedAt = System.currentTimeMillis(); - while (System.currentTimeMillis() < (startedAt + waitTime)) { - for (Motor motor : motors) { - for (RunState state : awaitingState) { - if (motor.getSlotStateTracker().getSlotState() == state) { - logger.trace(() -> "at least one 'any' of " + activityDef.getAlias() + "/Motor[" + motor.getSlotId() + "] is now in state " + motor.getSlotStateTracker().getSlotState()); - return true; - } - } - } - try { - Thread.sleep(pollTime); - } catch (InterruptedException ignored) { - } - } - logger.trace(() -> "none of " + activityDef.getAlias() + "/Motor [" + motors.size() + "] is in states in " + Arrays.asList(awaitingState)); - return false; - } - - /** * Await a required thread (aka motor/slot) entering a specific SlotState * @@ -505,8 +471,8 @@ public class ActivityExecutor implements ActivityController, ParameterMap.Listen boolean awaitedRequiredState = awaitMotorState(m, waitTime, pollTime, awaitingState); if (!awaitedRequiredState) { String error = "Unable to await " + activityDef.getAlias() + - "/Motor[" + m.getSlotId() + "]: from state " + startingState + " to " + m.getSlotStateTracker().getSlotState() - + " after waiting for " + waitTime + "ms"; + "/Motor[" + m.getSlotId() + "]: from state " + startingState + " to " + m.getSlotStateTracker().getSlotState() + + " after waiting for " + waitTime + "ms"; RuntimeException e = new RuntimeException(error); logger.error(error); throw e; @@ -532,7 +498,7 @@ public class ActivityExecutor implements ActivityController, ParameterMap.Listen public synchronized void notifyException(Thread t, Throwable e) { logger.debug(() -> "Uncaught exception in activity thread forwarded to activity executor: " + e.getMessage()); this.stoppingException = new RuntimeException("Error in activity thread " + t.getName(), e); - forceStopScenario(10000); + forceStopActivity(10000); } @Override @@ -564,4 +530,10 @@ public class ActivityExecutor implements ActivityController, ParameterMap.Listen } + @Override + public synchronized ExecResult call() throws Exception { + boolean stopped = awaitCompletion(Integer.MAX_VALUE); + ExecResult result = new ExecResult(startedAt, stoppedAt, "", this.stoppingException); + return result; + } } diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ScenarioResult.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ExecMetricsResult.java similarity index 68% rename from engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ScenarioResult.java rename to engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ExecMetricsResult.java index 54bf4d007..f20b943b5 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ScenarioResult.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ExecMetricsResult.java @@ -20,20 +20,16 @@ import com.codahale.metrics.*; import io.nosqlbench.api.engine.metrics.ActivityMetrics; import io.nosqlbench.engine.core.logging.Log4JMetricsReporter; import io.nosqlbench.engine.core.metrics.NBMetricsSummary; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import java.io.ByteArrayOutputStream; import java.io.PrintStream; import java.nio.charset.StandardCharsets; import java.util.HashSet; -import java.util.Optional; import java.util.Set; import java.util.concurrent.TimeUnit; -public class ScenarioResult { +public class ExecMetricsResult extends ExecResult { - private final static Logger logger = LogManager.getLogger(ScenarioResult.class); public static final Set INTERVAL_ONLY_METRICS = Set.of( MetricAttribute.MIN, MetricAttribute.MAX, @@ -51,32 +47,12 @@ public class ScenarioResult { MetricAttribute.M5_RATE, MetricAttribute.M15_RATE ); - private final long startedAt; - private final long endedAt; - private final Exception exception; - private final String iolog; - - public ScenarioResult(Exception e, String iolog, long startedAt, long endedAt) { - logger.debug("populating "+(e==null? "NORMAL" : "ERROR")+" scenario result"); - if (logger.isDebugEnabled()) { - StackTraceElement[] st = Thread.currentThread().getStackTrace(); - for (int i = 0; i < st.length; i++) { - logger.debug(":AT " + st[i].getFileName()+":"+st[i].getLineNumber()+":"+st[i].getMethodName()); - if (i>10) break; - } - } - this.iolog = ((iolog!=null) ? iolog + "\n\n" : "") + (e!=null? e.getMessage() : ""); - this.startedAt = startedAt; - this.endedAt = endedAt; - this.exception = e; + public ExecMetricsResult(long startedAt, long endedAt, String iolog, Exception e) { + super(startedAt, endedAt, iolog, e); } - public void reportElapsedMillis() { - logger.info("-- SCENARIO TOOK " + getElapsedMillis() + "ms --"); - } - - public String getSummaryReport() { + public String getMetricsSummary() { ByteArrayOutputStream os = new ByteArrayOutputStream(); PrintStream ps = new PrintStream(os); ConsoleReporter.Builder builder = ConsoleReporter.forRegistry(ActivityMetrics.getMetricRegistry()) @@ -91,45 +67,23 @@ public class ScenarioResult { builder.disabledMetricAttributes(disabled); ConsoleReporter consoleReporter = builder.build(); consoleReporter.report(); - ps.flush(); + consoleReporter.close(); String result = os.toString(StandardCharsets.UTF_8); return result; } public void reportToConsole() { - String summaryReport = getSummaryReport(); + String summaryReport = getMetricsSummary(); System.out.println(summaryReport); } - public Optional getException() { - return Optional.ofNullable(exception); + public void reportMetricsSummaryTo(PrintStream out) { + out.println(getMetricsSummary()); } - public void rethrowIfError() { - if (exception != null) { - if (exception instanceof RuntimeException) { - throw ((RuntimeException) exception); - } else { - throw new RuntimeException(exception); - } - } - } - - public String getIOLog() { - return this.iolog; - } - - public long getElapsedMillis() { - return endedAt - startedAt; - } - - public void reportTo(PrintStream out) { - out.println(getSummaryReport()); - } - - public void reportToLog() { + public void reportMetricsSummaryToLog() { logger.debug("-- WARNING: Metrics which are taken per-interval (like histograms) will not have --"); logger.debug("-- active data on this last report. (The workload has already stopped.) Record --"); logger.debug("-- metrics to an external format to see values for each reporting interval. --"); @@ -142,10 +96,11 @@ public class ScenarioResult { .outputTo(logger) .build(); reporter.report(); + reporter.close(); logger.debug("-- END METRICS DETAIL --"); } - public void reportCountsTo(PrintStream printStream) { + public void reportMetricsCountsTo(PrintStream printStream) { StringBuilder sb = new StringBuilder(); ActivityMetrics.getMetricRegistry().getMetrics().forEach((k, v) -> { diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ExecResult.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ExecResult.java new file mode 100644 index 000000000..db1454143 --- /dev/null +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ExecResult.java @@ -0,0 +1,68 @@ +/* + * 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; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.Optional; + +/** + * Provide a result type back to a caller, including the start and end times, + * any exception that occurred, and any content written to stdout or stderr equivalent + * IO streams. This is an execution result. + * + */ +public class ExecResult { + protected final static Logger logger = LogManager.getLogger(ExecMetricsResult.class); + protected final long startedAt; + protected final long endedAt; + protected final Exception exception; + protected final String iolog; + + public ExecResult(long startedAt, long endedAt, String iolog, Exception e) { + this.startedAt = startedAt; + this.endedAt = endedAt; + this.exception = e; + this.iolog = ((iolog != null) ? iolog + "\n\n" : "") + (e != null ? e.getMessage() : ""); + logger.debug("populating "+(e==null? "NORMAL" : "ERROR")+" scenario result"); + if (logger.isDebugEnabled()) { + StackTraceElement[] st = Thread.currentThread().getStackTrace(); + for (int i = 0; i < st.length; i++) { + logger.debug(":AT " + st[i].getFileName()+":"+st[i].getLineNumber()+":"+st[i].getMethodName()); + if (i>10) break; + } + } + + } + + public void reportElapsedMillisToLog() { + logger.info("-- SCENARIO TOOK " + getElapsedMillis() + "ms --"); + } + + public String getIOLog() { + return this.iolog; + } + + public long getElapsedMillis() { + return endedAt - startedAt; + } + + public Optional getException() { + return Optional.ofNullable(exception); + } +} diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ScenarioController.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ScenarioController.java index 0bda67c11..99929eeb4 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ScenarioController.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ScenarioController.java @@ -39,14 +39,15 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; /** - * A ScenarioController provides a way to start Activities, modify them while running, and forceStopMotors, pause or restart them. + * A ScenarioController provides a way to start Activities, + * modify them while running, and forceStopMotors, pause or restart them. */ public class ScenarioController { private static final Logger logger = LogManager.getLogger(ScenarioController.class); private static final Logger scenariologger = LogManager.getLogger("SCENARIO"); - private final Map activityExecutors = new ConcurrentHashMap<>(); + private final Map activityExecutors = new ConcurrentHashMap<>(); private final String sessionId; private final Maturity minMaturity; @@ -72,10 +73,9 @@ public class ScenarioController { .build()); - ActivityExecutor activityExecutor = getActivityExecutor(activityDef, true); + ActivityThreadsManager activityThreadsManager = getActivityExecutor(activityDef, true); scenariologger.debug("START " + activityDef.getAlias()); - activityExecutor.startActivity(); - + activityThreadsManager.startActivity(); } /** @@ -120,12 +120,12 @@ public class ScenarioController { .detail("params", activityDef.toString()) .build()); - ActivityExecutor activityExecutor = getActivityExecutor(activityDef, true); + ActivityThreadsManager activityThreadsManager = getActivityExecutor(activityDef, true); scenariologger.debug("RUN alias=" + activityDef.getAlias()); scenariologger.debug(" (RUN/START) alias=" + activityDef.getAlias()); - activityExecutor.startActivity(); + activityThreadsManager.startActivity(); scenariologger.debug(" (RUN/AWAIT before) alias=" + activityDef.getAlias()); - boolean completed = activityExecutor.awaitCompletion(timeout); + boolean completed = activityThreadsManager.awaitCompletion(timeout); scenariologger.debug(" (RUN/AWAIT after) completed=" + activityDef.getAlias()); } @@ -154,8 +154,8 @@ public class ScenarioController { public boolean isRunningActivity(ActivityDef activityDef) { - ActivityExecutor activityExecutor = getActivityExecutor(activityDef, false); - return activityExecutor != null && activityExecutor.isRunning(); + ActivityThreadsManager activityThreadsManager = getActivityExecutor(activityDef, false); + return activityThreadsManager != null && activityThreadsManager.isRunning(); } public boolean isRunningActivity(Map activityDefMap) { @@ -180,18 +180,18 @@ public class ScenarioController { .detail("params", activityDef.toString()) .build()); - ActivityExecutor activityExecutor = getActivityExecutor(activityDef, false); - if (activityExecutor == null) { + ActivityThreadsManager activityThreadsManager = getActivityExecutor(activityDef, false); + if (activityThreadsManager == null) { throw new RuntimeException("could not stop missing activity:" + activityDef); } - RunState runstate = activityExecutor.getActivity().getRunState(); + RunState runstate = activityThreadsManager.getActivity().getRunState(); if (runstate != RunState.Running) { - logger.warn("NOT stopping activity '" + activityExecutor.getActivity().getAlias() + "' because it is in state '" + runstate + "'"); + logger.warn("NOT stopping activity '" + activityThreadsManager.getActivity().getAlias() + "' because it is in state '" + runstate + "'"); return; } scenariologger.debug("STOP " + activityDef.getAlias()); - activityExecutor.stopActivity(); + activityThreadsManager.stopActivity(); } /** @@ -240,8 +240,8 @@ public class ScenarioController { if (param.equals("alias")) { throw new InvalidParameterException("It is not allowed to change the name of an existing activity."); } - ActivityExecutor activityExecutor = getActivityExecutor(alias); - ParameterMap params = activityExecutor.getActivityDef().getParams(); + ActivityThreadsManager activityThreadsManager = getActivityExecutor(alias); + ParameterMap params = activityThreadsManager.getActivityDef().getParams(); scenariologger.debug("SET (" + alias + "/" + param + ")=(" + value + ")"); params.set(param, value); } @@ -261,7 +261,7 @@ public class ScenarioController { throw new BasicError("alias must be provided"); } - ActivityExecutor executor = activityExecutors.get(alias); + ActivityThreadsManager executor = activityExecutors.get(alias); if (executor == null) { logger.info("started scenario from apply:" + alias); @@ -290,8 +290,8 @@ public class ScenarioController { * @return the associated ActivityExecutor * @throws RuntimeException a runtime exception if the named activity is not found */ - private ActivityExecutor getActivityExecutor(String activityAlias) { - Optional executor = + private ActivityThreadsManager getActivityExecutor(String activityAlias) { + Optional executor = Optional.ofNullable(activityExecutors.get(activityAlias)); return executor.orElseThrow( () -> new RuntimeException("ActivityExecutor for alias " + activityAlias + " not found.") @@ -315,9 +315,9 @@ public class ScenarioController { return matching; } - private ActivityExecutor getActivityExecutor(ActivityDef activityDef, boolean createIfMissing) { + private ActivityThreadsManager getActivityExecutor(ActivityDef activityDef, boolean createIfMissing) { synchronized (activityExecutors) { - ActivityExecutor executor = activityExecutors.get(activityDef.getAlias()); + ActivityThreadsManager executor = activityExecutors.get(activityDef.getAlias()); if (executor == null && createIfMissing) { if (activityDef.getParams().containsKey("driver")) { @@ -333,7 +333,7 @@ public class ScenarioController { ) ); - executor = new ActivityExecutor( + executor = new ActivityThreadsManager( activityType.getAssembledActivity( activityDef, getActivityMap() @@ -342,7 +342,7 @@ public class ScenarioController { ); activityExecutors.put(activityDef.getAlias(), executor); } else { - executor = new ActivityExecutor( + executor = new ActivityThreadsManager( new StandardActivityType(activityDef).getAssembledActivity( activityDef, getActivityMap() ), this.sessionId @@ -392,7 +392,7 @@ public class ScenarioController { */ public List getActivityDefs() { return activityExecutors.values().stream() - .map(ActivityExecutor::getActivityDef) + .map(ActivityThreadsManager::getActivityDef) .collect(Collectors.toList()); } @@ -425,7 +425,8 @@ public class ScenarioController { /** * Await completion of all running activities, but do not force shutdownActivity. This method is meant to provide - * the blocking point for calling logic. It waits. + * the blocking point for calling logic. It waits. If there is an error which should propagate into the scenario, + * then it should be thrown from this method. * * @param waitTimeMillis The time to wait, usually set very high * @return true, if all activities completed before the timer expired, false otherwise @@ -436,7 +437,7 @@ public class ScenarioController { long remaining = waitTimeMillis; List finishers = new ArrayList<>(); - for (ActivityExecutor ae : activityExecutors.values()) { + for (ActivityThreadsManager ae : activityExecutors.values()) { ActivityFinisher finisher = new ActivityFinisher(ae, (int) remaining); finishers.add(finisher); finisher.start(); @@ -492,12 +493,12 @@ public class ScenarioController { } public boolean awaitActivity(ActivityDef activityDef) { - ActivityExecutor activityExecutor = getActivityExecutor(activityDef, false); - if (activityExecutor == null) { + ActivityThreadsManager activityThreadsManager = getActivityExecutor(activityDef, false); + if (activityThreadsManager == null) { throw new RuntimeException("Could not await missing activity: " + activityDef); } scenariologger.debug("AWAIT/before alias=" + activityDef.getAlias()); - boolean finished = activityExecutor.awaitFinish(Integer.MAX_VALUE); + boolean finished = activityThreadsManager.awaitFinishedOrStopped(Integer.MAX_VALUE); scenariologger.debug("AWAIT/after completed=" + finished); return finished; @@ -506,7 +507,7 @@ public class ScenarioController { /** * @return an unmodifyable String to executor map of all activities known to this scenario */ - public Map getActivityExecutorMap() { + public Map getActivityExecutorMap() { return Collections.unmodifiableMap(activityExecutors); } @@ -516,7 +517,7 @@ public class ScenarioController { private Map getActivityMap() { Map activityMap = new HashMap(); - for (Map.Entry entry : activityExecutors.entrySet()) { + for (Map.Entry entry : activityExecutors.entrySet()) { activityMap.put(entry.getKey(), entry.getValue().getActivity()); } return activityMap; @@ -524,7 +525,7 @@ public class ScenarioController { public List getProgressMeters() { List indicators = new ArrayList<>(); - for (ActivityExecutor ae : activityExecutors.values()) { + for (ActivityThreadsManager ae : activityExecutors.values()) { indicators.add(ae.getProgressMeter()); } indicators.sort(Comparator.comparing(ProgressMeterDisplay::getStartTime)); diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ScenariosResults.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ScenariosResults.java index 6eb5774ea..80c44b8c8 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ScenariosResults.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ScenariosResults.java @@ -28,27 +28,26 @@ public class ScenariosResults { private static final Logger logger = LogManager.getLogger(ScenariosResults.class); private final String scenariosExecutorName; - private final Map scenarioResultMap = new LinkedHashMap<>(); + private final Map scenarioResultMap = new LinkedHashMap<>(); public ScenariosResults(ScenariosExecutor scenariosExecutor) { this.scenariosExecutorName = scenariosExecutor.getName(); } - public ScenariosResults(ScenariosExecutor scenariosExecutor, Map map) { + public ScenariosResults(ScenariosExecutor scenariosExecutor, Map map) { this.scenariosExecutorName = scenariosExecutor.getName(); scenarioResultMap.putAll(map); } public String getExecutionSummary() { - StringBuilder sb = new StringBuilder("executions: "); - sb.append(scenarioResultMap.size()).append(" scenarios, "); - sb.append(scenarioResultMap.values().stream().filter(r -> r.getException().isEmpty()).count()).append(" normal, "); - sb.append(scenarioResultMap.values().stream().filter(r -> r.getException().isPresent()).count()).append(" errored"); - return sb.toString(); + String sb = "executions: " + scenarioResultMap.size() + " scenarios, " + + scenarioResultMap.values().stream().filter(r -> r.getException().isEmpty()).count() + " normal, " + + scenarioResultMap.values().stream().filter(r -> r.getException().isPresent()).count() + " errored"; + return sb; } - public ScenarioResult getOne() { + public ExecMetricsResult getOne() { if (this.scenarioResultMap.size() != 1) { throw new RuntimeException("getOne found " + this.scenarioResultMap.size() + " results instead of 1."); } @@ -57,14 +56,14 @@ public class ScenariosResults { } public void reportToLog() { - for (Map.Entry entry : this.scenarioResultMap.entrySet()) { + for (Map.Entry entry : this.scenarioResultMap.entrySet()) { Scenario scenario = entry.getKey(); - ScenarioResult oresult = entry.getValue(); + ExecMetricsResult oresult = entry.getValue(); logger.info("results for scenario: " + scenario); if (oresult != null) { - oresult.reportElapsedMillis(); + oresult.reportElapsedMillisToLog(); } else { logger.error(scenario.getScenarioName() + ": incomplete (missing result)"); } diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/StartedActivityInfo.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/StartedActivityInfo.java new file mode 100644 index 000000000..28b7f76c9 --- /dev/null +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/StartedActivityInfo.java @@ -0,0 +1,29 @@ +/* + * 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; + +import io.nosqlbench.engine.api.activityapi.core.Activity; + +public class StartedActivityInfo { + private final Activity activity; + + StartedActivityInfo(Activity activity) { + this.activity = activity; + } + + +} diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/script/Scenario.java b/engine-core/src/main/java/io/nosqlbench/engine/core/script/Scenario.java index 93fb5183c..a087b444d 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/script/Scenario.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/script/Scenario.java @@ -27,9 +27,9 @@ import io.nosqlbench.engine.api.extensions.ScriptingPluginInfo; import io.nosqlbench.engine.api.scripting.ScriptEnvBuffer; import io.nosqlbench.engine.core.annotation.Annotators; import io.nosqlbench.engine.core.lifecycle.ActivityProgressIndicator; +import io.nosqlbench.engine.core.lifecycle.ExecMetricsResult; import io.nosqlbench.engine.core.lifecycle.PolyglotScenarioController; import io.nosqlbench.engine.core.lifecycle.ScenarioController; -import io.nosqlbench.engine.core.lifecycle.ScenarioResult; import io.nosqlbench.engine.core.metrics.PolyglotMetricRegistryBindings; import io.nosqlbench.nb.annotations.Maturity; import org.apache.logging.log4j.LogManager; @@ -58,7 +58,7 @@ import java.util.Optional; import java.util.concurrent.Callable; import java.util.stream.Collectors; -public class Scenario implements Callable { +public class Scenario implements Callable { private final String commandLine; private final String reportSummaryTo; @@ -71,9 +71,9 @@ public class Scenario implements Callable { private Exception error; private ScenarioMetadata scenarioMetadata; - private ScenarioResult result; + private ExecMetricsResult result; - public Optional getResultIfComplete() { + public Optional getResultIfComplete() { return Optional.ofNullable(this.result); } @@ -171,7 +171,7 @@ public class Scenario implements Callable { return this; } - private void initializeScriptingEngine() { + private void initializeScriptingEngine(ScenarioController scenarioController) { logger.debug("Using engine " + engine.toString()); MetricRegistry metricRegistry = ActivityMetrics.getMetricRegistry(); @@ -198,12 +198,10 @@ public class Scenario implements Callable { this.scriptEngine = GraalJSScriptEngine.create(polyglotEngine, contextSettings); - scenarioController = new ScenarioController(this.scenarioName, minMaturity); if (!progressInterval.equals("disabled")) { activityProgressIndicator = new ActivityProgressIndicator(scenarioController, progressInterval); } - scriptEnv = new ScenarioContext(scenarioController); scriptEngine.setContext(scriptEnv); @@ -264,9 +262,22 @@ public class Scenario implements Callable { .build() ); - initializeScriptingEngine(); logger.debug("Running control script for " + getScenarioName() + "."); + scenarioController = new ScenarioController(this.scenarioName, minMaturity); + initializeScriptingEngine(scenarioController); + executeScenarioScripts(); + long awaitCompletionTime = 86400 * 365 * 1000L; + logger.debug("Awaiting completion of scenario and activities for " + awaitCompletionTime + " millis."); + + scenarioController.awaitCompletion(awaitCompletionTime); + //TODO: Ensure control flow covers controller shutdown in event of internal error. + + Runtime.getRuntime().removeShutdownHook(scenarioShutdownHook); + scenarioShutdownHook.run(); + } + + private void executeScenarioScripts() { for (String script : scripts) { try { Object result = null; @@ -304,6 +315,7 @@ public class Scenario implements Callable { System.err.flush(); System.out.flush(); } catch (Exception e) { + this.error=e; this.state = State.Errored; logger.error("Error in scenario, shutting down. (" + e + ")"); try { @@ -311,7 +323,6 @@ public class Scenario implements Callable { } catch (Exception eInner) { logger.debug("Found inner exception while forcing stop with rethrow=false: " + eInner); } finally { - this.error = e; throw new RuntimeException(e); } } finally { @@ -320,14 +331,6 @@ public class Scenario implements Callable { endedAtMillis = System.currentTimeMillis(); } } - long awaitCompletionTime = 86400 * 365 * 1000L; - logger.debug("Awaiting completion of scenario and activities for " + awaitCompletionTime + " millis."); - scenarioController.awaitCompletion(awaitCompletionTime); - //TODO: Ensure control flow covers controller shutdown in event of internal error. - - Runtime.getRuntime().removeShutdownHook(scenarioShutdownHook); - scenarioShutdownHook = null; - finish(); } public void finish() { @@ -370,9 +373,23 @@ public class Scenario implements Callable { /** * This should be the only way to get a ScenarioResult for a Scenario. * + * The lifecycle of a scenario includes the lifecycles of all of the following: + *

    + *
  1. The scenario control script, executing within a graaljs context.
  2. + *
  3. The lifecycle of every activity which is started within the scenario.
  4. + *
+ * + * 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: + *
    + *
  • The scenario control script has run to completion, or experienced an exception.
  • + *
  • Each activity has run to completion, experienced an exception, or all
  • + *
+ * * @return */ - public synchronized ScenarioResult call() { + public synchronized ExecMetricsResult call() { if (result == null) { try { runScenario(); @@ -386,15 +403,15 @@ public class Scenario implements Callable { } String iolog = scriptEnv.getTimedLog(); - this.result = new ScenarioResult(this.error, iolog, this.startedAtMillis, this.endedAtMillis); - result.reportToLog(); + this.result = new ExecMetricsResult(this.error, iolog, this.startedAtMillis, this.endedAtMillis); + result.reportMetricsSummaryToLog(); doReportSummaries(reportSummaryTo, result); } return result; } - private void doReportSummaries(String reportSummaryTo, ScenarioResult result) { + private void doReportSummaries(String reportSummaryTo, ExecMetricsResult result) { List fullChannels = new ArrayList<>(); List briefChannels = new ArrayList<>(); @@ -437,7 +454,7 @@ public class Scenario implements Callable { } } } - fullChannels.forEach(result::reportTo); + fullChannels.forEach(result::reportMetricsSummaryTo); // briefChannels.forEach(result::reportCountsTo); } diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/script/ScenariosExecutor.java b/engine-core/src/main/java/io/nosqlbench/engine/core/script/ScenariosExecutor.java index 0d6581266..dedaf4ff3 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/script/ScenariosExecutor.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/script/ScenariosExecutor.java @@ -50,7 +50,7 @@ public class ScenariosExecutor { if (submitted.get(scenario.getScenarioName()) != null) { throw new BasicError("Scenario " + scenario.getScenarioName() + " is already defined. Remove it first to reuse the name."); } - Future future = executor.submit(scenario); + Future future = executor.submit(scenario); SubmittedScenario s = new SubmittedScenario(scenario, future); submitted.put(s.getName(), s); } @@ -106,7 +106,7 @@ public class ScenariosExecutor { throw new RuntimeException("executor still runningScenarios after awaiting all results for " + timeout + "ms. isTerminated:" + executor.isTerminated() + " isShutdown:" + executor.isShutdown()); } - Map scenarioResultMap = new LinkedHashMap<>(); + Map scenarioResultMap = new LinkedHashMap<>(); getAsyncResultStatus() .entrySet() .forEach( @@ -133,26 +133,26 @@ public class ScenariosExecutor { * All submitted scenarios are included. Those which are still pending * are returned with an empty option.

* - *

Results may be exceptional. If {@link ScenarioResult#getException()} is present, + *

Results may be exceptional. If {@link ExecMetricsResult#getException()} is present, * then the result did not complete normally.

* * @return map of async results, with incomplete results as Optional.empty() */ - public Map> getAsyncResultStatus() { + public Map> getAsyncResultStatus() { - Map> optResults = new LinkedHashMap<>(); + Map> optResults = new LinkedHashMap<>(); for (SubmittedScenario submittedScenario : submitted.values()) { - Future resultFuture = submittedScenario.getResultFuture(); + Future resultFuture = submittedScenario.getResultFuture(); - Optional oResult = Optional.empty(); + Optional 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"); - oResult = Optional.of(new ScenarioResult(e, "errored output", now, now)); + oResult = Optional.of(new ExecMetricsResult(now, now, "errored output", e)); } } @@ -179,7 +179,7 @@ public class ScenariosExecutor { * @param scenarioName the scenario name of interest * @return an optional result */ - public Optional> getPendingResult(String scenarioName) { + public Optional> getPendingResult(String scenarioName) { return Optional.ofNullable(submitted.get(scenarioName)).map(s -> s.resultFuture); } @@ -224,9 +224,9 @@ public class ScenariosExecutor { private static class SubmittedScenario { private final Scenario scenario; - private final Future resultFuture; + private final Future resultFuture; - SubmittedScenario(Scenario scenario, Future resultFuture) { + SubmittedScenario(Scenario scenario, Future resultFuture) { this.scenario = scenario; this.resultFuture = resultFuture; } @@ -235,7 +235,7 @@ public class ScenariosExecutor { return scenario; } - Future getResultFuture() { + Future getResultFuture() { return resultFuture; } diff --git a/engine-core/src/test/java/io/nosqlbench/engine/core/ActivityExecutorTest.java b/engine-core/src/test/java/io/nosqlbench/engine/core/ActivityThreadsManagerTest.java similarity index 94% rename from engine-core/src/test/java/io/nosqlbench/engine/core/ActivityExecutorTest.java rename to engine-core/src/test/java/io/nosqlbench/engine/core/ActivityThreadsManagerTest.java index a6e93e8d7..b82c1cd29 100644 --- a/engine-core/src/test/java/io/nosqlbench/engine/core/ActivityExecutorTest.java +++ b/engine-core/src/test/java/io/nosqlbench/engine/core/ActivityThreadsManagerTest.java @@ -28,7 +28,7 @@ import io.nosqlbench.engine.api.activityimpl.input.CoreInputDispenser; import io.nosqlbench.engine.api.activityimpl.input.AtomicInput; import io.nosqlbench.engine.api.activityimpl.motor.CoreMotor; import io.nosqlbench.engine.api.activityimpl.motor.CoreMotorDispenser; -import io.nosqlbench.engine.core.lifecycle.ActivityExecutor; +import io.nosqlbench.engine.core.lifecycle.ActivityThreadsManager; import io.nosqlbench.engine.core.lifecycle.ActivityTypeLoader; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; @@ -38,8 +38,8 @@ import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; -public class ActivityExecutorTest { - private static final Logger logger = LogManager.getLogger(ActivityExecutorTest.class); +public class ActivityThreadsManagerTest { + private static final Logger logger = LogManager.getLogger(ActivityThreadsManagerTest.class); @Test public synchronized void testRestart() { @@ -55,7 +55,7 @@ public class ActivityExecutorTest { a.setInputDispenserDelegate(idisp); a.setMotorDispenserDelegate(mdisp); - ActivityExecutor ae = new ActivityExecutor(a, "test-restart"); + ActivityThreadsManager ae = new ActivityThreadsManager(a, "test-restart"); ad.setThreads(1); ae.startActivity(); ae.stopActivity(); @@ -79,7 +79,7 @@ public class ActivityExecutorTest { a.setInputDispenserDelegate(idisp); a.setMotorDispenserDelegate(mdisp); - ActivityExecutor ae = new ActivityExecutor(a, "test-delayed-start"); + ActivityThreadsManager ae = new ActivityThreadsManager(a, "test-delayed-start"); ad.setThreads(1); ae.startActivity(); ae.awaitCompletion(15000); @@ -104,7 +104,7 @@ public class ActivityExecutorTest { a.setInputDispenserDelegate(idisp); a.setMotorDispenserDelegate(mdisp); - ActivityExecutor ae = new ActivityExecutor(a, "test-new-executor"); + ActivityThreadsManager ae = new ActivityThreadsManager(a, "test-new-executor"); ad.setThreads(5); ae.startActivity(); diff --git a/engine-rest/src/main/java/io/nosqlbench/engine/rest/resources/ScenarioExecutorEndpoint.java b/engine-rest/src/main/java/io/nosqlbench/engine/rest/resources/ScenarioExecutorEndpoint.java index 06a3f0d5e..52ecd9b22 100644 --- a/engine-rest/src/main/java/io/nosqlbench/engine/rest/resources/ScenarioExecutorEndpoint.java +++ b/engine-rest/src/main/java/io/nosqlbench/engine/rest/resources/ScenarioExecutorEndpoint.java @@ -21,7 +21,7 @@ import io.nosqlbench.engine.cli.BasicScriptBuffer; import io.nosqlbench.engine.cli.Cmd; import io.nosqlbench.engine.cli.NBCLICommandParser; import io.nosqlbench.engine.cli.ScriptBuffer; -import io.nosqlbench.engine.core.lifecycle.ScenarioResult; +import io.nosqlbench.engine.core.lifecycle.ExecMetricsResult; import io.nosqlbench.engine.core.script.Scenario; import io.nosqlbench.engine.core.script.ScenariosExecutor; import io.nosqlbench.engine.rest.services.WorkSpace; @@ -234,8 +234,8 @@ public class ScenarioExecutorEndpoint implements WebServiceObject { Optional pendingScenario = executor.getPendingScenario(scenarioName); if (pendingScenario.isPresent()) { - Optional> pendingResult = executor.getPendingResult(scenarioName); - Future scenarioResultFuture = pendingResult.get(); + Optional> pendingResult = executor.getPendingResult(scenarioName); + Future scenarioResultFuture = pendingResult.get(); return new LiveScenarioView(pendingScenario.get()); } else { throw new RuntimeException("Scenario name '" + scenarioName + "' not found."); diff --git a/engine-rest/src/main/java/io/nosqlbench/engine/rest/transfertypes/ResultView.java b/engine-rest/src/main/java/io/nosqlbench/engine/rest/transfertypes/ResultView.java index 1394ce7e6..e84da8113 100644 --- a/engine-rest/src/main/java/io/nosqlbench/engine/rest/transfertypes/ResultView.java +++ b/engine-rest/src/main/java/io/nosqlbench/engine/rest/transfertypes/ResultView.java @@ -16,13 +16,13 @@ package io.nosqlbench.engine.rest.transfertypes; -import io.nosqlbench.engine.core.lifecycle.ScenarioResult; +import io.nosqlbench.engine.core.lifecycle.ExecMetricsResult; public class ResultView { - private final ScenarioResult result; + private final ExecMetricsResult result; - public ResultView(ScenarioResult result) { + public ResultView(ExecMetricsResult result) { this.result = result; } diff --git a/nbr-examples/src/test/java/io/nosqlbench/nbr/examples/ScriptExampleTests.java b/nbr-examples/src/test/java/io/nosqlbench/nbr/examples/ScriptExampleTests.java index bc230712f..a84a712b2 100644 --- a/nbr-examples/src/test/java/io/nosqlbench/nbr/examples/ScriptExampleTests.java +++ b/nbr-examples/src/test/java/io/nosqlbench/nbr/examples/ScriptExampleTests.java @@ -16,7 +16,7 @@ package io.nosqlbench.nbr.examples; -import io.nosqlbench.engine.core.lifecycle.ScenarioResult; +import io.nosqlbench.engine.core.lifecycle.ExecMetricsResult; import io.nosqlbench.engine.core.lifecycle.ScenariosResults; import io.nosqlbench.engine.core.script.Scenario; import io.nosqlbench.engine.core.script.ScenariosExecutor; @@ -41,7 +41,7 @@ import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; public class ScriptExampleTests { - public static ScenarioResult runScenario(String scriptname, String... params) { + public static ExecMetricsResult runScenario(String scriptname, String... params) { if ((params.length % 2) != 0) { throw new RuntimeException("params must be pairwise key, value, ..."); } @@ -74,7 +74,7 @@ public class ScriptExampleTests { // s.addScriptText("load('classpath:scripts/async/" + scriptname + ".js');"); executor.execute(s); ScenariosResults scenariosResults = executor.awaitAllResults(); - ScenarioResult scenarioResult = scenariosResults.getOne(); + ExecMetricsResult scenarioResult = scenariosResults.getOne(); executor.shutdownNow(); return scenarioResult; } @@ -86,7 +86,7 @@ public class ScriptExampleTests { @Test public void testLinkedInput() { - ScenarioResult scenarioResult = runScenario("linkedinput"); + ExecMetricsResult scenarioResult = runScenario("linkedinput"); Pattern p = Pattern.compile(".*started leader.*started follower.*stopped leader.*stopped follower.*", Pattern.DOTALL); assertThat(p.matcher(scenarioResult.getIOLog()).matches()).isTrue(); @@ -94,14 +94,14 @@ public class ScriptExampleTests { @Test public void testExceptionPropagationFromMotorThread() { - ScenarioResult scenarioResult = runScenario("activityerror"); + ExecMetricsResult scenarioResult = runScenario("activityerror"); assertThat(scenarioResult.getException()).isPresent(); assertThat(scenarioResult.getException().get().getMessage()).contains("For input string: \"unparsable\""); } @Test public void testCycleRate() { - ScenarioResult scenarioResult = runScenario("cycle_rate"); + ExecMetricsResult scenarioResult = runScenario("cycle_rate"); String iolog = scenarioResult.getIOLog(); System.out.println("iolog\n" + iolog); Pattern p = Pattern.compile(".*mean cycle rate = (\\d[.\\d]+).*", Pattern.DOTALL); @@ -116,13 +116,13 @@ public class ScriptExampleTests { @Test public void testExtensionPoint() { - ScenarioResult scenarioResult = runScenario("extensions"); + ExecMetricsResult scenarioResult = runScenario("extensions"); assertThat(scenarioResult.getIOLog()).contains("sum is 46"); } @Test public void testOptimo() { - ScenarioResult scenarioResult = runScenario("optimo"); + ExecMetricsResult scenarioResult = runScenario("optimo"); String iolog = scenarioResult.getIOLog(); System.out.println("iolog\n" + iolog); assertThat(iolog).contains("map of result was"); @@ -130,14 +130,14 @@ public class ScriptExampleTests { @Test public void testExtensionCsvLogger() { - ScenarioResult scenarioResult = runScenario("extension_csvmetrics"); + ExecMetricsResult scenarioResult = runScenario("extension_csvmetrics"); assertThat(scenarioResult.getIOLog()).contains("started new " + "csvlogger: logs/csvmetricstestdir"); } @Test public void testScriptParamsVariable() { - ScenarioResult scenarioResult = runScenario("params_variable", "one", "two", "three", "four"); + ExecMetricsResult scenarioResult = runScenario("params_variable", "one", "two", "three", "four"); assertThat(scenarioResult.getIOLog()).contains("params[\"one\"]='two'"); assertThat(scenarioResult.getIOLog()).contains("params[\"three\"]='four'"); assertThat(scenarioResult.getIOLog()).contains("overridden[\"three\"] [overridden-three-five]='five'"); @@ -146,7 +146,7 @@ public class ScriptExampleTests { @Test public void testScriptParamsUndefVariableWithOverride() { - ScenarioResult scenarioResult = runScenario("undef_param", "one", "two", "three", "four"); + ExecMetricsResult scenarioResult = runScenario("undef_param", "one", "two", "three", "four"); assertThat(scenarioResult.getIOLog()).contains("before: params[\"three\"]:four"); assertThat(scenarioResult.getIOLog()).contains("before: params.three:four"); assertThat(scenarioResult.getIOLog()).contains("after: params[\"three\"]:undefined"); @@ -155,7 +155,7 @@ public class ScriptExampleTests { @Test public void testExtensionHistoStatsLogger() throws IOException { - ScenarioResult scenarioResult = runScenario("extension_histostatslogger"); + ExecMetricsResult scenarioResult = runScenario("extension_histostatslogger"); assertThat(scenarioResult.getIOLog()).contains("stdout started " + "logging to logs/histostats.csv"); List strings = Files.readAllLines(Paths.get( @@ -167,7 +167,7 @@ public class ScriptExampleTests { @Test public void testExtensionCsvOutput() throws IOException { - ScenarioResult scenarioResult = runScenario("extension_csvoutput"); + ExecMetricsResult scenarioResult = runScenario("extension_csvoutput"); List strings = Files.readAllLines(Paths.get( "logs/csvoutputtestfile.csv")); String logdata = strings.stream().collect(Collectors.joining("\n")); @@ -177,7 +177,7 @@ public class ScriptExampleTests { @Test public void testExtensionHistogramLogger() throws IOException { - ScenarioResult scenarioResult = runScenario("extension_histologger"); + ExecMetricsResult scenarioResult = runScenario("extension_histologger"); assertThat(scenarioResult.getIOLog()).contains("stdout started logging to hdrhistodata.log"); List strings = Files.readAllLines(Paths.get("hdrhistodata.log")); String logdata = strings.stream().collect(Collectors.joining("\n")); @@ -187,7 +187,7 @@ public class ScriptExampleTests { @Test public void testBlockingRun() { - ScenarioResult scenarioResult = runScenario("blockingrun"); + ExecMetricsResult scenarioResult = runScenario("blockingrun"); int a1end = scenarioResult.getIOLog().indexOf("blockingactivity1 finished"); int a2start = scenarioResult.getIOLog().indexOf("running blockingactivity2"); assertThat(a1end).isLessThan(a2start); @@ -195,12 +195,12 @@ public class ScriptExampleTests { @Test public void testAwaitFinished() { - ScenarioResult scenarioResult = runScenario("awaitfinished"); + ExecMetricsResult scenarioResult = runScenario("awaitfinished"); } @Test public void testStartStop() { - ScenarioResult scenarioResult = runScenario("startstopdiag"); + ExecMetricsResult scenarioResult = runScenario("startstopdiag"); int startedAt = scenarioResult.getIOLog().indexOf("starting activity teststartstopdiag"); int stoppedAt = scenarioResult.getIOLog().indexOf("stopped activity teststartstopdiag"); assertThat(startedAt).isGreaterThan(0); @@ -210,7 +210,7 @@ public class ScriptExampleTests { // TODO: find out why this causes a long delay after stop is called. @Test public void testThreadChange() { - ScenarioResult scenarioResult = runScenario("threadchange"); + ExecMetricsResult scenarioResult = runScenario("threadchange"); int changedTo1At = scenarioResult.getIOLog().indexOf("threads now 1"); int changedTo5At = scenarioResult.getIOLog().indexOf("threads now 5"); System.out.println("IOLOG:\n"+scenarioResult.getIOLog()); @@ -220,13 +220,13 @@ public class ScriptExampleTests { @Test public void testReadMetric() { - ScenarioResult scenarioResult = runScenario("readmetrics"); + ExecMetricsResult scenarioResult = runScenario("readmetrics"); assertThat(scenarioResult.getIOLog()).contains("count: "); } @Test public void testShutdownHook() { - ScenarioResult scenarioResult = runScenario("extension_shutdown_hook"); + ExecMetricsResult scenarioResult = runScenario("extension_shutdown_hook"); assertThat(scenarioResult.getIOLog()).doesNotContain("shutdown hook running").describedAs( "shutdown hooks should not run in the same IO context as the main scenario" ); @@ -234,7 +234,7 @@ public class ScriptExampleTests { @Test public void testExceptionPropagationFromActivityInit() { - ScenarioResult scenarioResult = runScenario("activityiniterror"); + ExecMetricsResult scenarioResult = runScenario("activityiniterror"); assertThat(scenarioResult.getException()).isPresent(); assertThat(scenarioResult.getException().get().getMessage()).contains("Unable to convert end cycle from invalid"); assertThat(scenarioResult.getException()).isNotNull(); @@ -242,7 +242,7 @@ public class ScriptExampleTests { @Test public void testReportedCoDelayBursty() { - ScenarioResult scenarioResult = runScenario("cocycledelay_bursty"); + ExecMetricsResult scenarioResult = runScenario("cocycledelay_bursty"); assertThat(scenarioResult.getIOLog()).contains("step1 metrics.waittime="); assertThat(scenarioResult.getIOLog()).contains("step2 metrics.waittime="); String iolog = scenarioResult.getIOLog(); @@ -252,7 +252,7 @@ public class ScriptExampleTests { @Test public void testReportedCoDelayStrict() { - ScenarioResult scenarioResult = runScenario("cocycledelay_strict"); + ExecMetricsResult scenarioResult = runScenario("cocycledelay_strict"); assertThat(scenarioResult.getIOLog()).contains("step1 cycles.waittime="); assertThat(scenarioResult.getIOLog()).contains("step2 cycles.waittime="); String iolog = scenarioResult.getIOLog(); @@ -263,14 +263,14 @@ public class ScriptExampleTests { @Test public void testCycleRateChangeNewMetrics() { - ScenarioResult scenarioResult = runScenario("cycle_rate_change"); + ExecMetricsResult scenarioResult = runScenario("cycle_rate_change"); String ioLog = scenarioResult.getIOLog(); assertThat(ioLog).contains("cycles adjusted, exiting on iteration"); } @Test public void testExitLogic() { - ScenarioResult scenarioResult = runScenario( + ExecMetricsResult scenarioResult = runScenario( "basicdiag", "type", "diag", "cyclerate", "5", "erroroncycle", "10", "cycles", "2000" ); diff --git a/nbr-examples/src/test/java/io/nosqlbench/nbr/examples/SpeedCheckIntegrationTests.java b/nbr-examples/src/test/java/io/nosqlbench/nbr/examples/SpeedCheckIntegrationTests.java index ee8fae02a..b750530d6 100644 --- a/nbr-examples/src/test/java/io/nosqlbench/nbr/examples/SpeedCheckIntegrationTests.java +++ b/nbr-examples/src/test/java/io/nosqlbench/nbr/examples/SpeedCheckIntegrationTests.java @@ -15,7 +15,7 @@ */ package io.nosqlbench.nbr.examples; -import io.nosqlbench.engine.core.lifecycle.ScenarioResult; +import io.nosqlbench.engine.core.lifecycle.ExecMetricsResult; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -30,14 +30,14 @@ public class SpeedCheckIntegrationTests { @Disabled // Verified as working public void testSpeedSanity() { - ScenarioResult scenarioResult = ScriptExampleTests.runScenario("speedcheck"); + ExecMetricsResult scenarioResult = ScriptExampleTests.runScenario("speedcheck"); } @Test @Disabled // This seems incomplete public void testThreadSpeeds() { - ScenarioResult scenarioResult = ScriptExampleTests.runScenario("threadspeeds"); + ExecMetricsResult scenarioResult = ScriptExampleTests.runScenario("threadspeeds"); } diff --git a/nbr/src/test/java/io/nosqlbench/cli/testing/ExitStatusIntegrationTests.java b/nbr/src/test/java/io/nosqlbench/cli/testing/ExitStatusIntegrationTests.java index 70a163eaf..c1b580e24 100644 --- a/nbr/src/test/java/io/nosqlbench/cli/testing/ExitStatusIntegrationTests.java +++ b/nbr/src/test/java/io/nosqlbench/cli/testing/ExitStatusIntegrationTests.java @@ -86,18 +86,18 @@ class ExitStatusIntegrationTests { assertThat(result.exitStatus).isEqualTo(2); } -// This will not work reliablyl until the activity shutdown bug is fixed. -// @Test -// public void testCloseErrorHandlerOnSpace() { -// ProcessInvoker invoker = new ProcessInvoker(); -// invoker.setLogDir("logs/test"); -// ProcessResult result = invoker.run("exitstatus_erroronclose", 30, -// java, "-jar", JARNAME, "--logs-dir", "logs/test/error_on_close", "run", -// "driver=diag", "threads=2", "rate=5", "op=noop", "cycles=10", "erroronclose=true", "-vvv" -// ); -// String stdout = String.join("\n", result.getStdoutData()); -// String stderr = String.join("\n", result.getStderrData()); -// assertThat(result.exception).isNotNull(); -// assertThat(result.exception.getMessage()).contains("diag space was configured to throw"); -// } + @Test + public void testCloseErrorHandlerOnSpace() { + ProcessInvoker invoker = new ProcessInvoker(); + invoker.setLogDir("logs/test"); + ProcessResult result = invoker.run("exitstatus_erroronclose", 30, + java, "-jar", JARNAME, "--logs-dir", "logs/test/error_on_close", "run", + "driver=diag", "threads=2", "rate=5", "op=noop", "cycles=10", "erroronclose=true", "-vvv" + ); + String stdout = String.join("\n", result.getStdoutData()); + String stderr = String.join("\n", result.getStderrData()); + assertThat(result.exception).isNotNull(); + assertThat(result.exception.getMessage()).contains("diag space was configured to throw"); + } + } From bd129c442ee4fa91a35f743273c9f934f2070f6b Mon Sep 17 00:00:00 2001 From: Jonathan Shook Date: Tue, 20 Dec 2022 20:00:34 -0600 Subject: [PATCH 02/19] run activities within a dedicated executor --- .../lifecycle/{ => scenario}/ScenarioController.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) rename engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/{ => scenario}/ScenarioController.java (97%) diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ScenarioController.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/ScenarioController.java similarity index 97% rename from engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ScenarioController.java rename to engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/ScenarioController.java index 99929eeb4..8869e50cd 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ScenarioController.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/ScenarioController.java @@ -51,9 +51,16 @@ public class ScenarioController { private final String sessionId; private final Maturity minMaturity; - public ScenarioController(String sessionId, Maturity minMaturity) { - this.sessionId = sessionId; + private ExecutorService activitiesExecutor; + + public ScenarioController(Scenario scenario, Maturity minMaturity) { + this.scenario = scenario; this.minMaturity = minMaturity; + this.activityLoader = new ActivityLoader(scenario); + + ActivitiesExceptionHandler exceptionHandler = new ActivitiesExceptionHandler(this); + IndexedThreadFactory indexedThreadFactory = new IndexedThreadFactory("ACTIVITY", exceptionHandler); + this.activitiesExecutor = Executors.newCachedThreadPool(indexedThreadFactory); } /** From 13219232298238006ac1565122fd1bb86edc2aa4 Mon Sep 17 00:00:00 2001 From: Jonathan Shook Date: Tue, 20 Dec 2022 20:01:22 -0600 Subject: [PATCH 03/19] remove unused code paths to simplify refactoring --- .../bindings}/PolyglotScenarioController.java | 14 -------------- 1 file changed, 14 deletions(-) rename engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/{ => scenario/script/bindings}/PolyglotScenarioController.java (94%) diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/PolyglotScenarioController.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/script/bindings/PolyglotScenarioController.java similarity index 94% rename from engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/PolyglotScenarioController.java rename to engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/script/bindings/PolyglotScenarioController.java index 5d1e4e5c8..38bb7b195 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/PolyglotScenarioController.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/script/bindings/PolyglotScenarioController.java @@ -146,20 +146,6 @@ public class PolyglotScenarioController { } } - public synchronized void apply(Object o) { - if (o instanceof Value) { - applyValue((Value) o); - } else if (o instanceof Map) { - controller.apply((Map) o); - } else { - throw new RuntimeException("unknown type: " + o.getClass().getCanonicalName()); - } - } - - private synchronized void applyValue(Value spec) { - Map map = spec.as(Map.class); - controller.apply(map); - } public synchronized void awaitActivity(Object o) { this.await(o); From ba42bfea478d7483c5cbf890a032364c4052ef24 Mon Sep 17 00:00:00 2001 From: Jonathan Shook Date: Tue, 20 Dec 2022 20:01:33 -0600 Subject: [PATCH 04/19] allow conversion of missing config types --- .../api/config/standard/ConfigModel.java | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/nb-api/src/main/java/io/nosqlbench/api/config/standard/ConfigModel.java b/nb-api/src/main/java/io/nosqlbench/api/config/standard/ConfigModel.java index 811191eac..7fb1577c6 100644 --- a/nb-api/src/main/java/io/nosqlbench/api/config/standard/ConfigModel.java +++ b/nb-api/src/main/java/io/nosqlbench/api/config/standard/ConfigModel.java @@ -131,10 +131,20 @@ public class ConfigModel implements NBConfigModel { return (T) Double.valueOf(string); } else if (type == BigDecimal.class) { return (T) BigDecimal.valueOf(Double.parseDouble(string)); + } else if (type == boolean.class || type == Boolean.class) { + return (T) Boolean.valueOf(Boolean.parseBoolean(string)); } else { throw new RuntimeException("CharSequence type " + type.getSimpleName() + " could " + " not be converted from " + value.getClass().getSimpleName()); } + } else if (value instanceof Boolean bool) { + if (type == boolean.class) { + return (T) bool; + } else { + throw new RuntimeException("Boolean type " + type.getSimpleName() + " could " + + " not be converted from " + value.getClass().getSimpleName()); + + } } } catch (Exception e) { @@ -318,11 +328,10 @@ public class ConfigModel implements NBConfigModel { @Override public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("[").append( - params.stream().map(p -> p.getNames().get(0)).collect(Collectors.joining(","))) - .append("]"); + String sb = "[" + + params.stream().map(p -> p.getNames().get(0)).collect(Collectors.joining(",")) + + "]"; - return sb.toString(); + return sb; } } From 3f14fefcb122f38158047294a48962167ca6f89b Mon Sep 17 00:00:00 2001 From: Jonathan Shook Date: Tue, 20 Dec 2022 20:01:43 -0600 Subject: [PATCH 05/19] delegate activity instancing to dedicated loader --- .../lifecycle/activity/ActivityLoader.java | 53 +++++++++++++++++++ .../{ => activity}/ActivityTypeLoader.java | 14 +---- .../scenario/ScenarioController.java | 8 +-- 3 files changed, 59 insertions(+), 16 deletions(-) create mode 100644 engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/activity/ActivityLoader.java rename engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/{ => activity}/ActivityTypeLoader.java (87%) diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/activity/ActivityLoader.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/activity/ActivityLoader.java new file mode 100644 index 000000000..fb0f9bd95 --- /dev/null +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/activity/ActivityLoader.java @@ -0,0 +1,53 @@ +/* + * 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.activity; + +import io.nosqlbench.api.engine.activityimpl.ActivityDef; +import io.nosqlbench.engine.api.activityapi.core.Activity; +import io.nosqlbench.engine.api.activityimpl.uniform.StandardActivityType; +import io.nosqlbench.engine.core.lifecycle.scenario.Scenario; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Consolidates the activity type and activity instantiation logic into one place + * per scope. Within the lifetime of this ActivityLoader, all activities may + * see each other by name. + */ +public class ActivityLoader { + private final static Logger logger = LogManager.getLogger("ACTIVITIES"); + private final Map activityMap = new ConcurrentHashMap<>(); + private final Scenario scenario; + + public ActivityLoader(Scenario scenario) { + this.scenario = scenario; + } + + public synchronized Activity loadActivity(ActivityDef activityDef) { + Activity activity = new StandardActivityType(activityDef).getAssembledActivity(activityDef, activityMap); + activityMap.put(activity.getAlias(),activity); + logger.debug("Resolved activity for alias '" + activityDef.getAlias() + "'"); + return activity; + } + + public void purgeActivity(String activityAlias) { + this.activityMap.remove(activityAlias); + } +} diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ActivityTypeLoader.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/activity/ActivityTypeLoader.java similarity index 87% rename from engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ActivityTypeLoader.java rename to engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/activity/ActivityTypeLoader.java index 071144417..59e873428 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ActivityTypeLoader.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/activity/ActivityTypeLoader.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.nosqlbench.engine.core.lifecycle; +package io.nosqlbench.engine.core.lifecycle.activity; import io.nosqlbench.engine.api.activityapi.core.ActivityType; import io.nosqlbench.api.engine.activityimpl.ActivityDef; @@ -150,18 +150,6 @@ public class ActivityTypeLoader { if (oda.isPresent()) { DriverAdapter driverAdapter = oda.get(); -// activityDef.getParams().remove("driver"); -// if (driverAdapter instanceof NBConfigurable) { -// NBConfigModel cfgModel = ((NBConfigurable) driverAdapter).getConfigModel(); -// Optional op_yaml_loc = activityDef.getParams().getOptionalString("yaml", "workload"); -// if (op_yaml_loc.isPresent()) { -// Map disposable = new LinkedHashMap<>(activityDef.getParams()); -// StmtsDocList workload = StatementsLoader.loadPath(logger, op_yaml_loc.get(), disposable, "activities"); -// cfgModel=cfgModel.add(workload.getConfigModel()); -// } -// NBConfiguration cfg = cfgModel.apply(activityDef.getParams()); -// ((NBConfigurable) driverAdapter).applyConfig(cfg); -// } ActivityType activityType = new StandardActivityType<>(driverAdapter, activityDef); return Optional.of(activityType); } else { diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/ScenarioController.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/ScenarioController.java index 8869e50cd..150929fda 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/ScenarioController.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/ScenarioController.java @@ -47,11 +47,13 @@ public class ScenarioController { private static final Logger logger = LogManager.getLogger(ScenarioController.class); private static final Logger scenariologger = LogManager.getLogger("SCENARIO"); - private final Map activityExecutors = new ConcurrentHashMap<>(); - private final String sessionId; + private final ActivityLoader activityLoader; + + private final Map activityInfoMap = new ConcurrentHashMap<>(); + private final Scenario scenario; private final Maturity minMaturity; - private ExecutorService activitiesExecutor; + private final ExecutorService activitiesExecutor; public ScenarioController(Scenario scenario, Maturity minMaturity) { this.scenario = scenario; From a1cde761f51c3454f18f60ad1577f6edbb9a5b1c Mon Sep 17 00:00:00 2001 From: Jonathan Shook Date: Tue, 20 Dec 2022 20:01:57 -0600 Subject: [PATCH 06/19] fix unapplied bug in diag space --- .../src/main/java/io/nosqlbench/adapter/diag/DiagSpace.java | 1 + 1 file changed, 1 insertion(+) diff --git a/adapter-diag/src/main/java/io/nosqlbench/adapter/diag/DiagSpace.java b/adapter-diag/src/main/java/io/nosqlbench/adapter/diag/DiagSpace.java index 4a8ca3d2c..eab297012 100644 --- a/adapter-diag/src/main/java/io/nosqlbench/adapter/diag/DiagSpace.java +++ b/adapter-diag/src/main/java/io/nosqlbench/adapter/diag/DiagSpace.java @@ -38,6 +38,7 @@ public class DiagSpace implements ActivityDefObserver, AutoCloseable { public DiagSpace(String name, NBConfiguration cfg) { this.cfg = cfg; this.name = name; + applyConfig(cfg); logger.trace("diag space initialized as '" + name + "'"); } From 31dc3ce8f7f7b70ba15270bb7f7832515a8182a5 Mon Sep 17 00:00:00 2001 From: Jonathan Shook Date: Tue, 20 Dec 2022 20:02:16 -0600 Subject: [PATCH 07/19] package housekeeping --- .../java/io/nosqlbench/engine/cli/NBCLI.java | 20 +++++++------- .../nosqlbench/engine/cli/NBCLIOptions.java | 2 +- .../ActivityProgressIndicator.java | 3 ++- .../{ => activity}/ActivityStatus.java | 2 +- .../{ => process}/NBCLIErrorHandler.java | 2 +- .../{ => process}/ShutdownManager.java | 2 +- .../scenario/ScenarioController.java | 27 +++++++++---------- .../scenario}/ScenarioShutdownHook.java | 2 +- .../scenario}/ScenariosExecutor.java | 6 +++-- .../{ => scenario}/ScenariosResults.java | 5 ++-- .../scenario}/script/MetricsMapper.java | 6 ++--- .../script/SandboxExtensionFinder.java | 2 +- .../scenario}/script/ScenarioContext.java | 4 +-- .../script/ScenarioExceptionHandler.java | 4 ++- .../script/ScriptExecutionError.java | 2 +- .../scenario}/script/ScriptParams.java | 5 ++-- .../PolyglotMetricRegistryBindings.java | 3 ++- .../bindings/PolyglotScenarioController.java | 3 ++- .../script/bindings}/ReadOnlyBindings.java | 2 +- .../bindings}/ReadOnlyBindingsException.java | 2 +- .../engine/core/metrics/MetricReporters.java | 6 ++--- .../nosqlbench/engine/core/ScenarioTest.java | 2 +- .../core/script/ScenariosExecutorTest.java | 4 ++- .../resources/ScenarioExecutorEndpoint.java | 6 ++--- .../rest/transfertypes/LiveScenarioView.java | 2 +- .../script/MetricsMapperIntegrationTest.java | 1 + 26 files changed, 66 insertions(+), 59 deletions(-) rename engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/{ => activity}/ActivityProgressIndicator.java (97%) rename engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/{ => activity}/ActivityStatus.java (92%) rename engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/{ => process}/NBCLIErrorHandler.java (98%) rename engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/{ => process}/ShutdownManager.java (95%) rename engine-core/src/main/java/io/nosqlbench/engine/core/{script => lifecycle/scenario}/ScenarioShutdownHook.java (94%) rename engine-core/src/main/java/io/nosqlbench/engine/core/{script => lifecycle/scenario}/ScenariosExecutor.java (97%) rename engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/{ => scenario}/ScenariosResults.java (94%) rename engine-core/src/main/java/io/nosqlbench/engine/core/{ => lifecycle/scenario}/script/MetricsMapper.java (95%) rename engine-core/src/main/java/io/nosqlbench/engine/core/{ => lifecycle/scenario}/script/SandboxExtensionFinder.java (96%) rename engine-core/src/main/java/io/nosqlbench/engine/core/{ => lifecycle/scenario}/script/ScenarioContext.java (90%) rename engine-core/src/main/java/io/nosqlbench/engine/core/{ => lifecycle/scenario}/script/ScenarioExceptionHandler.java (88%) rename engine-core/src/main/java/io/nosqlbench/engine/core/{ => lifecycle/scenario}/script/ScriptExecutionError.java (93%) rename engine-core/src/main/java/io/nosqlbench/engine/core/{ => lifecycle/scenario}/script/ScriptParams.java (97%) rename engine-core/src/main/java/io/nosqlbench/engine/core/{metrics => lifecycle/scenario/script/bindings}/PolyglotMetricRegistryBindings.java (97%) rename engine-core/src/main/java/io/nosqlbench/engine/core/{script => lifecycle/scenario/script/bindings}/ReadOnlyBindings.java (95%) rename engine-core/src/main/java/io/nosqlbench/engine/core/{script => lifecycle/scenario/script/bindings}/ReadOnlyBindingsException.java (93%) diff --git a/engine-cli/src/main/java/io/nosqlbench/engine/cli/NBCLI.java b/engine-cli/src/main/java/io/nosqlbench/engine/cli/NBCLI.java index 08df6ddc6..ef73399dc 100644 --- a/engine-cli/src/main/java/io/nosqlbench/engine/cli/NBCLI.java +++ b/engine-cli/src/main/java/io/nosqlbench/engine/cli/NBCLI.java @@ -32,14 +32,17 @@ import io.nosqlbench.engine.api.activityapi.input.InputType; import io.nosqlbench.engine.api.activityapi.output.OutputType; import io.nosqlbench.engine.api.activityconfig.rawyaml.RawStmtsLoader; import io.nosqlbench.engine.core.annotation.Annotators; -import io.nosqlbench.engine.core.lifecycle.*; +import io.nosqlbench.engine.core.lifecycle.process.NBCLIErrorHandler; +import io.nosqlbench.engine.core.lifecycle.activity.ActivityTypeLoader; +import io.nosqlbench.engine.core.lifecycle.process.ShutdownManager; +import io.nosqlbench.engine.core.lifecycle.scenario.ScenariosResults; import io.nosqlbench.engine.core.logging.LoggerConfig; -import io.nosqlbench.engine.core.metadata.MarkdownDocInfo; +import io.nosqlbench.engine.core.metadata.MarkdownFinder; import io.nosqlbench.engine.core.metrics.MetricReporters; -import io.nosqlbench.engine.core.script.MetricsMapper; -import io.nosqlbench.engine.core.script.Scenario; -import io.nosqlbench.engine.core.script.ScenariosExecutor; -import io.nosqlbench.engine.core.script.ScriptParams; +import io.nosqlbench.engine.core.lifecycle.scenario.script.MetricsMapper; +import io.nosqlbench.engine.core.lifecycle.scenario.Scenario; +import io.nosqlbench.engine.core.lifecycle.scenario.ScenariosExecutor; +import io.nosqlbench.engine.core.lifecycle.scenario.script.ScriptParams; import io.nosqlbench.engine.docker.DockerMetricsManager; import io.nosqlbench.nb.annotations.Maturity; import io.nosqlbench.nb.annotations.Service; @@ -56,7 +59,6 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.*; -import java.util.concurrent.locks.LockSupport; import java.util.function.Function; import java.util.stream.Collectors; @@ -353,7 +355,7 @@ public class NBCLI implements Function { } if (options.wantsTopicalHelp()) { - Optional helpDoc = MarkdownDocInfo.forHelpTopic(options.wantsTopicalHelpFor()); + Optional helpDoc = MarkdownFinder.forHelpTopic(options.wantsTopicalHelpFor()); System.out.println(helpDoc.orElseThrow( () -> new RuntimeException("No help could be found for " + options.wantsTopicalHelpFor()) )); @@ -486,7 +488,7 @@ public class NBCLI implements Function { logger.info(scenariosResults.getExecutionSummary()); if (scenariosResults.hasError()) { - Exception exception = scenariosResults.getOne().getException().get(); + Exception exception = scenariosResults.getOne().getException(); logger.warn(scenariosResults.getExecutionSummary()); NBCLIErrorHandler.handle(exception, options.wantsStackTraces()); System.err.println(exception.getMessage()); // TODO: make this consistent with ConsoleLogging sequencing diff --git a/engine-cli/src/main/java/io/nosqlbench/engine/cli/NBCLIOptions.java b/engine-cli/src/main/java/io/nosqlbench/engine/cli/NBCLIOptions.java index 8cb3520ec..50abcebb0 100644 --- a/engine-cli/src/main/java/io/nosqlbench/engine/cli/NBCLIOptions.java +++ b/engine-cli/src/main/java/io/nosqlbench/engine/cli/NBCLIOptions.java @@ -18,7 +18,7 @@ package io.nosqlbench.engine.cli; import io.nosqlbench.engine.api.metrics.IndicatorMode; import io.nosqlbench.api.engine.util.Unit; -import io.nosqlbench.engine.core.script.Scenario; +import io.nosqlbench.engine.core.lifecycle.scenario.Scenario; import io.nosqlbench.nb.annotations.Maturity; import io.nosqlbench.api.system.NBEnvironment; import io.nosqlbench.api.errors.BasicError; diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ActivityProgressIndicator.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/activity/ActivityProgressIndicator.java similarity index 97% rename from engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ActivityProgressIndicator.java rename to engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/activity/ActivityProgressIndicator.java index 494b7df3d..7d50e799c 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ActivityProgressIndicator.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/activity/ActivityProgressIndicator.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.nosqlbench.engine.core.lifecycle; +package io.nosqlbench.engine.core.lifecycle.activity; import io.nosqlbench.engine.api.activityapi.core.RunState; import io.nosqlbench.engine.api.activityapi.core.progress.ProgressMeterDisplay; @@ -22,6 +22,7 @@ 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.ScenarioController; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ActivityStatus.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/activity/ActivityStatus.java similarity index 92% rename from engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ActivityStatus.java rename to engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/activity/ActivityStatus.java index d77927fbc..446df7ffc 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ActivityStatus.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/activity/ActivityStatus.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.nosqlbench.engine.core.lifecycle; +package io.nosqlbench.engine.core.lifecycle.activity; public class ActivityStatus { } diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/NBCLIErrorHandler.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/process/NBCLIErrorHandler.java similarity index 98% rename from engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/NBCLIErrorHandler.java rename to engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/process/NBCLIErrorHandler.java index 1a786377f..226adbe6e 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/NBCLIErrorHandler.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/process/NBCLIErrorHandler.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.nosqlbench.engine.core.lifecycle; +package io.nosqlbench.engine.core.lifecycle.process; import io.nosqlbench.api.errors.BasicError; import org.apache.logging.log4j.LogManager; diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ShutdownManager.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/process/ShutdownManager.java similarity index 95% rename from engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ShutdownManager.java rename to engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/process/ShutdownManager.java index 3d5b004bf..bc4568f5a 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ShutdownManager.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/process/ShutdownManager.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.nosqlbench.engine.core.lifecycle; +package io.nosqlbench.engine.core.lifecycle.process; import io.nosqlbench.engine.api.activityapi.core.Shutdownable; diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/ScenarioController.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/ScenarioController.java index 150929fda..64198b474 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/ScenarioController.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/ScenarioController.java @@ -13,28 +13,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.nosqlbench.engine.core.lifecycle; +package io.nosqlbench.engine.core.lifecycle.scenario; -import io.nosqlbench.engine.api.activityapi.core.Activity; -import io.nosqlbench.engine.api.activityapi.core.ActivityType; -import io.nosqlbench.engine.api.activityapi.core.RunState; -import io.nosqlbench.engine.api.activityapi.core.progress.ProgressMeterDisplay; -import io.nosqlbench.api.engine.activityimpl.ActivityDef; -import io.nosqlbench.api.engine.activityimpl.ParameterMap; -import io.nosqlbench.engine.api.activityimpl.uniform.StandardActivityType; -import io.nosqlbench.api.engine.metrics.ActivityMetrics; -import io.nosqlbench.engine.core.annotation.Annotators; -import io.nosqlbench.nb.annotations.Maturity; import io.nosqlbench.api.annotations.Annotation; import io.nosqlbench.api.annotations.Layer; -import io.nosqlbench.api.config.standard.ConfigSuggestions; -import io.nosqlbench.api.errors.BasicError; +import io.nosqlbench.api.engine.activityimpl.ActivityDef; +import io.nosqlbench.api.engine.activityimpl.ParameterMap; +import io.nosqlbench.api.engine.metrics.ActivityMetrics; +import io.nosqlbench.engine.api.activityapi.core.Activity; +import io.nosqlbench.engine.api.activityapi.core.progress.ProgressMeterDisplay; +import io.nosqlbench.engine.core.annotation.Annotators; +import io.nosqlbench.engine.core.lifecycle.ExecutionResult; +import io.nosqlbench.engine.core.lifecycle.IndexedThreadFactory; +import io.nosqlbench.engine.core.lifecycle.activity.*; +import io.nosqlbench.nb.annotations.Maturity; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import java.security.InvalidParameterException; import java.util.*; -import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.*; import java.util.regex.Pattern; import java.util.stream.Collectors; diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/script/ScenarioShutdownHook.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/ScenarioShutdownHook.java similarity index 94% rename from engine-core/src/main/java/io/nosqlbench/engine/core/script/ScenarioShutdownHook.java rename to engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/ScenarioShutdownHook.java index 679cb9dd3..e064f88b0 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/script/ScenarioShutdownHook.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/ScenarioShutdownHook.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.nosqlbench.engine.core.script; +package io.nosqlbench.engine.core.lifecycle.scenario; import org.apache.logging.log4j.Logger; diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/script/ScenariosExecutor.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/ScenariosExecutor.java similarity index 97% rename from engine-core/src/main/java/io/nosqlbench/engine/core/script/ScenariosExecutor.java rename to engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/ScenariosExecutor.java index dedaf4ff3..8c45134b1 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/script/ScenariosExecutor.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/ScenariosExecutor.java @@ -14,10 +14,12 @@ * limitations under the License. */ -package io.nosqlbench.engine.core.script; +package io.nosqlbench.engine.core.lifecycle.scenario; -import io.nosqlbench.engine.core.lifecycle.*; import io.nosqlbench.api.errors.BasicError; +import io.nosqlbench.engine.core.lifecycle.ExecutionMetricsResult; +import io.nosqlbench.engine.core.lifecycle.IndexedThreadFactory; +import io.nosqlbench.engine.core.lifecycle.scenario.script.ScenarioExceptionHandler; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ScenariosResults.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/ScenariosResults.java similarity index 94% rename from engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ScenariosResults.java rename to engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/ScenariosResults.java index 80c44b8c8..2d916e851 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ScenariosResults.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/ScenariosResults.java @@ -14,10 +14,9 @@ * limitations under the License. */ -package io.nosqlbench.engine.core.lifecycle; +package io.nosqlbench.engine.core.lifecycle.scenario; -import io.nosqlbench.engine.core.script.Scenario; -import io.nosqlbench.engine.core.script.ScenariosExecutor; +import io.nosqlbench.engine.core.lifecycle.ExecutionMetricsResult; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/script/MetricsMapper.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/script/MetricsMapper.java similarity index 95% rename from engine-core/src/main/java/io/nosqlbench/engine/core/script/MetricsMapper.java rename to engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/script/MetricsMapper.java index 105ee23f0..1aa41324a 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/script/MetricsMapper.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/script/MetricsMapper.java @@ -13,15 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.nosqlbench.engine.core.script; +package io.nosqlbench.engine.core.lifecycle.scenario.script; import com.codahale.metrics.*; import io.nosqlbench.engine.api.activityapi.core.Activity; import io.nosqlbench.engine.api.activityapi.core.ActivityType; import io.nosqlbench.api.engine.activityimpl.ActivityDef; import io.nosqlbench.api.engine.metrics.ActivityMetrics; -import io.nosqlbench.engine.core.lifecycle.ActivityTypeLoader; -import io.nosqlbench.engine.core.metrics.PolyglotMetricRegistryBindings; +import io.nosqlbench.engine.core.lifecycle.activity.ActivityTypeLoader; +import io.nosqlbench.engine.core.lifecycle.scenario.script.bindings.PolyglotMetricRegistryBindings; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/script/SandboxExtensionFinder.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/script/SandboxExtensionFinder.java similarity index 96% rename from engine-core/src/main/java/io/nosqlbench/engine/core/script/SandboxExtensionFinder.java rename to engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/script/SandboxExtensionFinder.java index 8323d3c2b..e351d6db7 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/script/SandboxExtensionFinder.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/script/SandboxExtensionFinder.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.nosqlbench.engine.core.script; +package io.nosqlbench.engine.core.lifecycle.scenario.script; import io.nosqlbench.engine.api.extensions.ScriptingPluginInfo; diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/script/ScenarioContext.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/script/ScenarioContext.java similarity index 90% rename from engine-core/src/main/java/io/nosqlbench/engine/core/script/ScenarioContext.java rename to engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/script/ScenarioContext.java index f3e6a2c1a..78b80211d 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/script/ScenarioContext.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/script/ScenarioContext.java @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.nosqlbench.engine.core.script; +package io.nosqlbench.engine.core.lifecycle.scenario.script; -import io.nosqlbench.engine.core.lifecycle.ScenarioController; +import io.nosqlbench.engine.core.lifecycle.scenario.ScenarioController; import io.nosqlbench.engine.api.scripting.ScriptEnvBuffer; public class ScenarioContext extends ScriptEnvBuffer { diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/script/ScenarioExceptionHandler.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/script/ScenarioExceptionHandler.java similarity index 88% rename from engine-core/src/main/java/io/nosqlbench/engine/core/script/ScenarioExceptionHandler.java rename to engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/script/ScenarioExceptionHandler.java index 2f3832c45..5cf420461 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/script/ScenarioExceptionHandler.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/script/ScenarioExceptionHandler.java @@ -14,7 +14,9 @@ * limitations under the License. */ -package io.nosqlbench.engine.core.script; +package io.nosqlbench.engine.core.lifecycle.scenario.script; + +import io.nosqlbench.engine.core.lifecycle.scenario.ScenariosExecutor; public class ScenarioExceptionHandler implements Thread.UncaughtExceptionHandler { private final ScenariosExecutor scenariosExecutor; diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/script/ScriptExecutionError.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/script/ScriptExecutionError.java similarity index 93% rename from engine-core/src/main/java/io/nosqlbench/engine/core/script/ScriptExecutionError.java rename to engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/script/ScriptExecutionError.java index 7b826e042..1ddf59a18 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/script/ScriptExecutionError.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/script/ScriptExecutionError.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.nosqlbench.engine.core.script; +package io.nosqlbench.engine.core.lifecycle.scenario.script; public class ScriptExecutionError extends RuntimeException { diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/script/ScriptParams.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/script/ScriptParams.java similarity index 97% rename from engine-core/src/main/java/io/nosqlbench/engine/core/script/ScriptParams.java rename to engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/script/ScriptParams.java index 69234c24e..a6d731fca 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/script/ScriptParams.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/script/ScriptParams.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.nosqlbench.engine.core.script; +package io.nosqlbench.engine.core.lifecycle.scenario.script; import com.google.gson.Gson; import com.google.gson.GsonBuilder; @@ -43,8 +43,7 @@ public class ScriptParams extends HashMap implements ProxyObject Map map; if (overrides instanceof Map) { map = (Map) overrides; - } else if (overrides instanceof Value) { - Value v = (Value) overrides; + } else if (overrides instanceof Value v) { map = v.as(Map.class); } else { throw new RuntimeException("Unrecognized overrides type: " + overrides.getClass().getCanonicalName()); diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/metrics/PolyglotMetricRegistryBindings.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/script/bindings/PolyglotMetricRegistryBindings.java similarity index 97% rename from engine-core/src/main/java/io/nosqlbench/engine/core/metrics/PolyglotMetricRegistryBindings.java rename to engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/script/bindings/PolyglotMetricRegistryBindings.java index 7b26f51c9..2167dca95 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/metrics/PolyglotMetricRegistryBindings.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/script/bindings/PolyglotMetricRegistryBindings.java @@ -14,10 +14,11 @@ * limitations under the License. */ -package io.nosqlbench.engine.core.metrics; +package io.nosqlbench.engine.core.lifecycle.scenario.script.bindings; import com.codahale.metrics.*; import com.codahale.metrics.Timer; +import io.nosqlbench.engine.core.metrics.MetricMap; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.graalvm.polyglot.Value; diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/script/bindings/PolyglotScenarioController.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/script/bindings/PolyglotScenarioController.java index 38bb7b195..b1c9ef12d 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/script/bindings/PolyglotScenarioController.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/script/bindings/PolyglotScenarioController.java @@ -14,9 +14,10 @@ * limitations under the License. */ -package io.nosqlbench.engine.core.lifecycle; +package io.nosqlbench.engine.core.lifecycle.scenario.script.bindings; import io.nosqlbench.api.engine.activityimpl.ActivityDef; +import io.nosqlbench.engine.core.lifecycle.scenario.ScenarioController; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.graalvm.polyglot.Value; diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/script/ReadOnlyBindings.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/script/bindings/ReadOnlyBindings.java similarity index 95% rename from engine-core/src/main/java/io/nosqlbench/engine/core/script/ReadOnlyBindings.java rename to engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/script/bindings/ReadOnlyBindings.java index 54e61c19f..b45330a00 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/script/ReadOnlyBindings.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/script/bindings/ReadOnlyBindings.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.nosqlbench.engine.core.script; +package io.nosqlbench.engine.core.lifecycle.scenario.script.bindings; import javax.script.Bindings; import java.util.Map; diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/script/ReadOnlyBindingsException.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/script/bindings/ReadOnlyBindingsException.java similarity index 93% rename from engine-core/src/main/java/io/nosqlbench/engine/core/script/ReadOnlyBindingsException.java rename to engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/script/bindings/ReadOnlyBindingsException.java index 73d05b507..60ee1049d 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/script/ReadOnlyBindingsException.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/script/bindings/ReadOnlyBindingsException.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.nosqlbench.engine.core.script; +package io.nosqlbench.engine.core.lifecycle.scenario.script.bindings; public class ReadOnlyBindingsException extends RuntimeException { private final Object parent; diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/metrics/MetricReporters.java b/engine-core/src/main/java/io/nosqlbench/engine/core/metrics/MetricReporters.java index 84754cb07..70994fbbe 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/metrics/MetricReporters.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/metrics/MetricReporters.java @@ -21,7 +21,7 @@ import com.codahale.metrics.graphite.Graphite; import com.codahale.metrics.graphite.GraphiteReporter; import io.nosqlbench.engine.api.activityapi.core.Shutdownable; import io.nosqlbench.api.engine.metrics.ActivityMetrics; -import io.nosqlbench.engine.core.lifecycle.ShutdownManager; +import io.nosqlbench.engine.core.lifecycle.process.ShutdownManager; import io.nosqlbench.engine.core.logging.Log4JMetricsReporter; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; @@ -54,7 +54,7 @@ public class MetricReporters implements Shutdownable { } public MetricReporters addGraphite(String dest, String prefix) { - logger.debug("Adding graphite reporter to " + dest + " with prefix " + prefix); + logger.debug(() -> "Adding graphite reporter to " + dest + " with prefix " + prefix); if (dest.indexOf(":")>=0) { String[] split = dest.split(":"); addGraphite(split[0],Integer.valueOf(split[1]),prefix); @@ -65,7 +65,7 @@ public class MetricReporters implements Shutdownable { } public void addCSVReporter(String directoryName, String prefix) { - logger.debug("Adding CSV reporter to " + directoryName + " with prefix " + prefix); + logger.debug(() -> "Adding CSV reporter to " + directoryName + " with prefix " + prefix); if (metricRegistries.isEmpty()) { throw new RuntimeException("There are no metric registries."); diff --git a/engine-core/src/test/java/io/nosqlbench/engine/core/ScenarioTest.java b/engine-core/src/test/java/io/nosqlbench/engine/core/ScenarioTest.java index 2678f0719..b210c95c5 100644 --- a/engine-core/src/test/java/io/nosqlbench/engine/core/ScenarioTest.java +++ b/engine-core/src/test/java/io/nosqlbench/engine/core/ScenarioTest.java @@ -17,7 +17,7 @@ package io.nosqlbench.engine.core; import io.nosqlbench.engine.api.scripting.ScriptEnvBuffer; -import io.nosqlbench.engine.core.script.Scenario; +import io.nosqlbench.engine.core.lifecycle.scenario.Scenario; import io.nosqlbench.nb.annotations.Maturity; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; diff --git a/engine-core/src/test/java/io/nosqlbench/engine/core/script/ScenariosExecutorTest.java b/engine-core/src/test/java/io/nosqlbench/engine/core/script/ScenariosExecutorTest.java index a391e5f0b..ac7e0ed5e 100644 --- a/engine-core/src/test/java/io/nosqlbench/engine/core/script/ScenariosExecutorTest.java +++ b/engine-core/src/test/java/io/nosqlbench/engine/core/script/ScenariosExecutorTest.java @@ -16,7 +16,9 @@ package io.nosqlbench.engine.core.script; -import io.nosqlbench.engine.core.lifecycle.ScenariosResults; +import io.nosqlbench.engine.core.lifecycle.scenario.ScenariosResults; +import io.nosqlbench.engine.core.lifecycle.scenario.Scenario; +import io.nosqlbench.engine.core.lifecycle.scenario.ScenariosExecutor; import io.nosqlbench.nb.annotations.Maturity; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; diff --git a/engine-rest/src/main/java/io/nosqlbench/engine/rest/resources/ScenarioExecutorEndpoint.java b/engine-rest/src/main/java/io/nosqlbench/engine/rest/resources/ScenarioExecutorEndpoint.java index 52ecd9b22..561354767 100644 --- a/engine-rest/src/main/java/io/nosqlbench/engine/rest/resources/ScenarioExecutorEndpoint.java +++ b/engine-rest/src/main/java/io/nosqlbench/engine/rest/resources/ScenarioExecutorEndpoint.java @@ -21,9 +21,9 @@ import io.nosqlbench.engine.cli.BasicScriptBuffer; import io.nosqlbench.engine.cli.Cmd; import io.nosqlbench.engine.cli.NBCLICommandParser; import io.nosqlbench.engine.cli.ScriptBuffer; -import io.nosqlbench.engine.core.lifecycle.ExecMetricsResult; -import io.nosqlbench.engine.core.script.Scenario; -import io.nosqlbench.engine.core.script.ScenariosExecutor; +import io.nosqlbench.engine.core.lifecycle.ExecutionMetricsResult; +import io.nosqlbench.engine.core.lifecycle.scenario.Scenario; +import io.nosqlbench.engine.core.lifecycle.scenario.ScenariosExecutor; import io.nosqlbench.engine.rest.services.WorkSpace; import io.nosqlbench.engine.rest.services.WorkspaceFinder; import io.nosqlbench.engine.rest.transfertypes.LiveScenarioView; diff --git a/engine-rest/src/main/java/io/nosqlbench/engine/rest/transfertypes/LiveScenarioView.java b/engine-rest/src/main/java/io/nosqlbench/engine/rest/transfertypes/LiveScenarioView.java index bcd0e9a1f..2af423f65 100644 --- a/engine-rest/src/main/java/io/nosqlbench/engine/rest/transfertypes/LiveScenarioView.java +++ b/engine-rest/src/main/java/io/nosqlbench/engine/rest/transfertypes/LiveScenarioView.java @@ -19,7 +19,7 @@ package io.nosqlbench.engine.rest.transfertypes; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonPropertyDescription; import io.nosqlbench.engine.api.activityapi.core.progress.ProgressMeterDisplay; -import io.nosqlbench.engine.core.script.Scenario; +import io.nosqlbench.engine.core.lifecycle.scenario.Scenario; import java.util.ArrayList; import java.util.Collection; diff --git a/nbr/src/test/java/io/nosqlbench/engine/core/script/MetricsMapperIntegrationTest.java b/nbr/src/test/java/io/nosqlbench/engine/core/script/MetricsMapperIntegrationTest.java index 78050f837..ce80b70e5 100644 --- a/nbr/src/test/java/io/nosqlbench/engine/core/script/MetricsMapperIntegrationTest.java +++ b/nbr/src/test/java/io/nosqlbench/engine/core/script/MetricsMapperIntegrationTest.java @@ -16,6 +16,7 @@ package io.nosqlbench.engine.core.script; +import io.nosqlbench.engine.core.lifecycle.scenario.script.MetricsMapper; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; From c076b1079c2eb8756d487e1644291095c64353dd Mon Sep 17 00:00:00 2001 From: Jonathan Shook Date: Tue, 20 Dec 2022 20:02:28 -0600 Subject: [PATCH 08/19] make http extension docs distinct from http adapter docs --- .../engine/extensions/http/{http.md => http-extension.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename engine-extensions/src/main/java/io/nosqlbench/engine/extensions/http/{http.md => http-extension.md} (100%) diff --git a/engine-extensions/src/main/java/io/nosqlbench/engine/extensions/http/http.md b/engine-extensions/src/main/java/io/nosqlbench/engine/extensions/http/http-extension.md similarity index 100% rename from engine-extensions/src/main/java/io/nosqlbench/engine/extensions/http/http.md rename to engine-extensions/src/main/java/io/nosqlbench/engine/extensions/http/http-extension.md From 1416c71d9149dfdce5042335b363039356368d51 Mon Sep 17 00:00:00 2001 From: Jonathan Shook Date: Tue, 20 Dec 2022 20:02:48 -0600 Subject: [PATCH 09/19] internalize activity start logic --- .../scenario/ScenarioController.java | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/ScenarioController.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/ScenarioController.java index 64198b474..d7ab41ca1 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/ScenarioController.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/ScenarioController.java @@ -78,10 +78,21 @@ public class ScenarioController { .detail("params", activityDef.toString()) .build()); + doStartActivity(activityDef); + } - ActivityThreadsManager activityThreadsManager = getActivityExecutor(activityDef, true); - scenariologger.debug("START " + activityDef.getAlias()); - activityThreadsManager.startActivity(); + + private synchronized ActivityRuntimeInfo doStartActivity(ActivityDef activityDef) { + if (!this.activityInfoMap.containsKey(activityDef.getAlias())) { + Activity activity = this.activityLoader.loadActivity(activityDef); + ActivityExecutor executor = new ActivityExecutor(activity, this.scenario.getScenarioName()); + Future 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()); } /** @@ -107,7 +118,7 @@ public class ScenarioController { public synchronized void run(int timeout, Map activityDefMap) { ActivityDef ad = new ActivityDef(new ParameterMap(activityDefMap)); - run(timeout, ad); + run(ad, timeout); } /** @@ -126,13 +137,8 @@ public class ScenarioController { .detail("params", activityDef.toString()) .build()); - ActivityThreadsManager activityThreadsManager = getActivityExecutor(activityDef, true); - scenariologger.debug("RUN alias=" + activityDef.getAlias()); - scenariologger.debug(" (RUN/START) alias=" + activityDef.getAlias()); - activityThreadsManager.startActivity(); - scenariologger.debug(" (RUN/AWAIT before) alias=" + activityDef.getAlias()); - boolean completed = activityThreadsManager.awaitCompletion(timeout); - scenariologger.debug(" (RUN/AWAIT after) completed=" + activityDef.getAlias()); + doStartActivity(activityDef); + awaitActivity(activityDef, timeoutMs); } public synchronized void run(int timeout, String activityDefString) { From 1816381f97cc62247d3d448e010565ce716dfbc2 Mon Sep 17 00:00:00 2001 From: Jonathan Shook Date: Tue, 20 Dec 2022 20:03:12 -0600 Subject: [PATCH 10/19] make integrated tests more robust --- .../src/test/resources/scripts/examples/activityerror.js | 2 +- .../src/test/resources/scripts/examples/startstopdiag.js | 7 ++++--- .../nosqlbench/cli/testing/ExitStatusIntegrationTests.java | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/nbr-examples/src/test/resources/scripts/examples/activityerror.js b/nbr-examples/src/test/resources/scripts/examples/activityerror.js index 265729eb4..4e0050a11 100644 --- a/nbr-examples/src/test/resources/scripts/examples/activityerror.js +++ b/nbr-examples/src/test/resources/scripts/examples/activityerror.js @@ -19,7 +19,7 @@ activitydef1 = { "driver" : "diag", "cycles" : "0..1500000", "threads" : "1", - "targetrate" : "500", + "targetrate" : "10", "op" : { "log": "type=log modulo=1" } diff --git a/nbr-examples/src/test/resources/scripts/examples/startstopdiag.js b/nbr-examples/src/test/resources/scripts/examples/startstopdiag.js index 4ef99242e..f7d17ebc6 100644 --- a/nbr-examples/src/test/resources/scripts/examples/startstopdiag.js +++ b/nbr-examples/src/test/resources/scripts/examples/startstopdiag.js @@ -18,17 +18,18 @@ activitydef = { "alias" : "teststartstopdiag", "driver" : "diag", "cycles" : "0..1000000000", - "threads" : "25", + "threads" : "5", "interval" : "2000", - "op" : "noop" + "op" : "noop", + "rate" : "5" }; print('starting activity teststartstopdiag'); scenario.start(activitydef); print('waiting 500 ms'); - scenario.waitMillis(500); + print('waited, stopping activity teststartstopdiag'); scenario.stop(activitydef); diff --git a/nbr/src/test/java/io/nosqlbench/cli/testing/ExitStatusIntegrationTests.java b/nbr/src/test/java/io/nosqlbench/cli/testing/ExitStatusIntegrationTests.java index c1b580e24..39d254b2e 100644 --- a/nbr/src/test/java/io/nosqlbench/cli/testing/ExitStatusIntegrationTests.java +++ b/nbr/src/test/java/io/nosqlbench/cli/testing/ExitStatusIntegrationTests.java @@ -96,8 +96,8 @@ class ExitStatusIntegrationTests { ); String stdout = String.join("\n", result.getStdoutData()); String stderr = String.join("\n", result.getStderrData()); - assertThat(result.exception).isNotNull(); - assertThat(result.exception.getMessage()).contains("diag space was configured to throw"); + assertThat(result.exitStatus).isEqualTo(2); + assertThat(stderr).contains("diag space was configured to throw"); } } From 077b41a3c64ce7b534f3debd9aacec65d6b200de Mon Sep 17 00:00:00 2001 From: Jonathan Shook Date: Tue, 20 Dec 2022 20:03:22 -0600 Subject: [PATCH 11/19] misc naming, typo and formatting improvements --- .../ActivityExceptionHandler.java | 8 +-- .../scenario/ScenarioController.java | 8 +-- .../lifecycle/scenario/ScenariosExecutor.java | 24 +++---- .../lifecycle/scenario/ScenariosResults.java | 10 +-- .../script/bindings/ActivityBindings.java} | 8 +-- .../bindings/PolyglotScenarioController.java | 6 +- ...rkdownDocInfo.java => MarkdownFinder.java} | 10 +-- .../engine/core/script/ScriptParamsTest.java | 2 +- .../resources/ScenarioExecutorEndpoint.java | 4 +- .../engine/rest/transfertypes/ResultView.java | 12 ++-- .../nbr/examples/ScriptExampleTests.java | 64 +++++++++---------- .../examples/SpeedCheckIntegrationTests.java | 6 +- 12 files changed, 81 insertions(+), 81 deletions(-) rename engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/{ => activity}/ActivityExceptionHandler.java (87%) rename engine-core/src/main/java/io/nosqlbench/engine/core/{script/NashornActivityBindings.java => lifecycle/scenario/script/bindings/ActivityBindings.java} (93%) rename engine-core/src/main/java/io/nosqlbench/engine/core/metadata/{MarkdownDocInfo.java => MarkdownFinder.java} (89%) diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ActivityExceptionHandler.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/activity/ActivityExceptionHandler.java similarity index 87% rename from engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ActivityExceptionHandler.java rename to engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/activity/ActivityExceptionHandler.java index b088829f3..751c26cbf 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ActivityExceptionHandler.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/activity/ActivityExceptionHandler.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.nosqlbench.engine.core.lifecycle; +package io.nosqlbench.engine.core.lifecycle.activity; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -23,9 +23,9 @@ public class ActivityExceptionHandler implements Thread.UncaughtExceptionHandler private static final Logger logger = LogManager.getLogger(ActivityExceptionHandler.class); - private final ActivityThreadsManager executor; + private final ActivityExecutor executor; - public ActivityExceptionHandler(ActivityThreadsManager executor) { + public ActivityExceptionHandler(ActivityExecutor executor) { this.executor = executor; logger.debug(() -> "Activity exception handler starting up for executor '" + executor + "'"); } @@ -33,7 +33,7 @@ public class ActivityExceptionHandler implements Thread.UncaughtExceptionHandler @Override public void uncaughtException(Thread t, Throwable e) { - logger.error("Uncaught exception in thread '" + t.getName() + ", state[" + t.getState() + "], notifying executor '" + executor + "'"); + logger.error("Uncaught exception in thread '" + t.getName() + ", state[" + t.getState() + "], notifying executor '" + executor + "': " + e); executor.notifyException(t, e); } } diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/ScenarioController.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/ScenarioController.java index d7ab41ca1..bef6a8c2e 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/ScenarioController.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/ScenarioController.java @@ -124,10 +124,10 @@ public class ScenarioController { /** * Synchronously run the defined activity with a timeout in seconds. * - * @param timeout seconds to await completion of the activity. + * @param timeoutMs seconds to await completion of the activity. * @param activityDef A definition for an activity to run */ - public synchronized void run(int timeout, ActivityDef activityDef) { + public synchronized void run(ActivityDef activityDef, long timeoutMs) { Annotators.recordAnnotation(Annotation.newBuilder() .session(sessionId) .now() @@ -143,7 +143,7 @@ public class ScenarioController { public synchronized void run(int timeout, String activityDefString) { ActivityDef activityDef = ActivityDef.parseActivityDef(activityDefString); - run(timeout, activityDef); + run(activityDef, timeout); } public synchronized void run(Map activityDefMap) { @@ -156,7 +156,7 @@ public class ScenarioController { public synchronized void run(ActivityDef activityDef) { - run(Integer.MAX_VALUE, activityDef); + run(activityDef, Long.MAX_VALUE); } diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/ScenariosExecutor.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/ScenariosExecutor.java index 8c45134b1..b644b84d1 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/ScenariosExecutor.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/ScenariosExecutor.java @@ -52,7 +52,7 @@ public class ScenariosExecutor { if (submitted.get(scenario.getScenarioName()) != null) { throw new BasicError("Scenario " + scenario.getScenarioName() + " is already defined. Remove it first to reuse the name."); } - Future future = executor.submit(scenario); + Future future = executor.submit(scenario); SubmittedScenario s = new SubmittedScenario(scenario, future); submitted.put(s.getName(), s); } @@ -108,7 +108,7 @@ public class ScenariosExecutor { throw new RuntimeException("executor still runningScenarios after awaiting all results for " + timeout + "ms. isTerminated:" + executor.isTerminated() + " isShutdown:" + executor.isShutdown()); } - Map scenarioResultMap = new LinkedHashMap<>(); + Map scenarioResultMap = new LinkedHashMap<>(); getAsyncResultStatus() .entrySet() .forEach( @@ -135,26 +135,26 @@ public class ScenariosExecutor { * All submitted scenarios are included. Those which are still pending * are returned with an empty option.

* - *

Results may be exceptional. If {@link ExecMetricsResult#getException()} is present, + *

Results may be exceptional. If {@link ExecutionMetricsResult#getException()} is present, * then the result did not complete normally.

* * @return map of async results, with incomplete results as Optional.empty() */ - public Map> getAsyncResultStatus() { + public Map> getAsyncResultStatus() { - Map> optResults = new LinkedHashMap<>(); + Map> optResults = new LinkedHashMap<>(); for (SubmittedScenario submittedScenario : submitted.values()) { - Future resultFuture = submittedScenario.getResultFuture(); + Future resultFuture = submittedScenario.getResultFuture(); - Optional oResult = Optional.empty(); + Optional 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"); - oResult = Optional.of(new ExecMetricsResult(now, now, "errored output", e)); + oResult = Optional.of(new ExecutionMetricsResult(now, now, "errored output", e)); } } @@ -181,7 +181,7 @@ public class ScenariosExecutor { * @param scenarioName the scenario name of interest * @return an optional result */ - public Optional> getPendingResult(String scenarioName) { + public Optional> getPendingResult(String scenarioName) { return Optional.ofNullable(submitted.get(scenarioName)).map(s -> s.resultFuture); } @@ -226,9 +226,9 @@ public class ScenariosExecutor { private static class SubmittedScenario { private final Scenario scenario; - private final Future resultFuture; + private final Future resultFuture; - SubmittedScenario(Scenario scenario, Future resultFuture) { + SubmittedScenario(Scenario scenario, Future resultFuture) { this.scenario = scenario; this.resultFuture = resultFuture; } @@ -237,7 +237,7 @@ public class ScenariosExecutor { return scenario; } - Future getResultFuture() { + Future getResultFuture() { return resultFuture; } diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/ScenariosResults.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/ScenariosResults.java index 2d916e851..2cd5e763b 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/ScenariosResults.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/ScenariosResults.java @@ -27,14 +27,14 @@ public class ScenariosResults { private static final Logger logger = LogManager.getLogger(ScenariosResults.class); private final String scenariosExecutorName; - private final Map scenarioResultMap = new LinkedHashMap<>(); + private final Map scenarioResultMap = new LinkedHashMap<>(); public ScenariosResults(ScenariosExecutor scenariosExecutor) { this.scenariosExecutorName = scenariosExecutor.getName(); } - public ScenariosResults(ScenariosExecutor scenariosExecutor, Map map) { + public ScenariosResults(ScenariosExecutor scenariosExecutor, Map map) { this.scenariosExecutorName = scenariosExecutor.getName(); scenarioResultMap.putAll(map); } @@ -46,7 +46,7 @@ public class ScenariosResults { return sb; } - public ExecMetricsResult getOne() { + public ExecutionMetricsResult getOne() { if (this.scenarioResultMap.size() != 1) { throw new RuntimeException("getOne found " + this.scenarioResultMap.size() + " results instead of 1."); } @@ -55,9 +55,9 @@ public class ScenariosResults { } public void reportToLog() { - for (Map.Entry entry : this.scenarioResultMap.entrySet()) { + for (Map.Entry entry : this.scenarioResultMap.entrySet()) { Scenario scenario = entry.getKey(); - ExecMetricsResult oresult = entry.getValue(); + ExecutionMetricsResult oresult = entry.getValue(); logger.info("results for scenario: " + scenario); diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/script/NashornActivityBindings.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/script/bindings/ActivityBindings.java similarity index 93% rename from engine-core/src/main/java/io/nosqlbench/engine/core/script/NashornActivityBindings.java rename to engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/script/bindings/ActivityBindings.java index d069f594e..3b7b9c581 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/script/NashornActivityBindings.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/script/bindings/ActivityBindings.java @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.nosqlbench.engine.core.script; +package io.nosqlbench.engine.core.lifecycle.scenario.script.bindings; import io.nosqlbench.api.engine.activityimpl.ActivityDef; -import io.nosqlbench.engine.core.lifecycle.ScenarioController; +import io.nosqlbench.engine.core.lifecycle.scenario.ScenarioController; import org.graalvm.polyglot.Value; import org.graalvm.polyglot.proxy.ProxyObject; @@ -27,12 +27,12 @@ import java.util.stream.Collectors; /** * Provide a bindings wrapper around a ScenarioController, */ -public class NashornActivityBindings implements Bindings, ProxyObject { +public class ActivityBindings implements Bindings, ProxyObject { private final ScenarioController scenario; private final Map elementMap = new HashMap(); - public NashornActivityBindings(ScenarioController scenarioController) { + public ActivityBindings(ScenarioController scenarioController) { this.scenario = scenarioController; } diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/script/bindings/PolyglotScenarioController.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/script/bindings/PolyglotScenarioController.java index b1c9ef12d..8d2504724 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/script/bindings/PolyglotScenarioController.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/script/bindings/PolyglotScenarioController.java @@ -81,7 +81,7 @@ public class PolyglotScenarioController { private synchronized void runValue(int timeout, Value spec) { logger.debug("run(Value) called with:" + spec); if (spec.isHostObject()) { - controller.run(timeout, (ActivityDef) spec.asHostObject()); + controller.run(spec.asHostObject(),timeout); } else if (spec.isString()) { controller.run(timeout, spec.asString()); } else if (spec.hasMembers()) { @@ -89,7 +89,7 @@ public class PolyglotScenarioController { } else if (spec.isHostObject()) { Object o = spec.asHostObject(); if (o instanceof ActivityDef) { - controller.run(timeout, (ActivityDef) o); + controller.run((ActivityDef) o, timeout); } else { throw new RuntimeException("unrecognized polyglot host object type for run: " + spec); } @@ -165,7 +165,7 @@ public class PolyglotScenarioController { private synchronized void awaitValue(Value spec) { if (spec.isHostObject()) { - controller.await((ActivityDef) spec.asHostObject()); + controller.await(spec.asHostObject(), Long.MAX_VALUE); } else if (spec.hasMembers()) { controller.await(spec.as(Map.class)); } else if (spec.isString()) { diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/metadata/MarkdownDocInfo.java b/engine-core/src/main/java/io/nosqlbench/engine/core/metadata/MarkdownFinder.java similarity index 89% rename from engine-core/src/main/java/io/nosqlbench/engine/core/metadata/MarkdownDocInfo.java rename to engine-core/src/main/java/io/nosqlbench/engine/core/metadata/MarkdownFinder.java index 45dd25263..26f719f79 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/metadata/MarkdownDocInfo.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/metadata/MarkdownFinder.java @@ -18,7 +18,7 @@ package io.nosqlbench.engine.core.metadata; import io.nosqlbench.engine.api.activityapi.core.ActivityType; import io.nosqlbench.api.engine.activityimpl.ActivityDef; -import io.nosqlbench.engine.core.lifecycle.ActivityTypeLoader; +import io.nosqlbench.engine.core.lifecycle.activity.ActivityTypeLoader; import io.nosqlbench.nb.annotations.Service; import io.nosqlbench.api.content.Content; import io.nosqlbench.api.content.NBIO; @@ -28,20 +28,20 @@ import org.apache.logging.log4j.Logger; import java.util.Optional; -public class MarkdownDocInfo { - private final static Logger logger = LogManager.getLogger(MarkdownDocInfo.class); +public class MarkdownFinder { + private final static Logger logger = LogManager.getLogger(MarkdownFinder.class); public static Optional forHelpTopic(String topic) { String help = null; try { - help = new MarkdownDocInfo().forActivityInstance(topic); + help = new MarkdownFinder().forActivityInstance(topic); return Optional.ofNullable(help); } catch (Exception e) { logger.debug("Did not find help topic for activity instance: " + topic); } try { - help = new MarkdownDocInfo().forResourceMarkdown(topic, "docs/"); + help = new MarkdownFinder().forResourceMarkdown(topic, "docs/"); return Optional.ofNullable(help); } catch (Exception e) { logger.debug("Did not find help topic for generic markdown file: " + topic + "(.md)"); diff --git a/engine-core/src/test/java/io/nosqlbench/engine/core/script/ScriptParamsTest.java b/engine-core/src/test/java/io/nosqlbench/engine/core/script/ScriptParamsTest.java index ee5da9aa9..1778689b4 100644 --- a/engine-core/src/test/java/io/nosqlbench/engine/core/script/ScriptParamsTest.java +++ b/engine-core/src/test/java/io/nosqlbench/engine/core/script/ScriptParamsTest.java @@ -17,12 +17,12 @@ package io.nosqlbench.engine.core.script; import io.nosqlbench.api.errors.BasicError; +import io.nosqlbench.engine.core.lifecycle.scenario.script.ScriptParams; import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.Map; -import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; public class ScriptParamsTest { diff --git a/engine-rest/src/main/java/io/nosqlbench/engine/rest/resources/ScenarioExecutorEndpoint.java b/engine-rest/src/main/java/io/nosqlbench/engine/rest/resources/ScenarioExecutorEndpoint.java index 561354767..ba1e4b132 100644 --- a/engine-rest/src/main/java/io/nosqlbench/engine/rest/resources/ScenarioExecutorEndpoint.java +++ b/engine-rest/src/main/java/io/nosqlbench/engine/rest/resources/ScenarioExecutorEndpoint.java @@ -234,8 +234,8 @@ public class ScenarioExecutorEndpoint implements WebServiceObject { Optional pendingScenario = executor.getPendingScenario(scenarioName); if (pendingScenario.isPresent()) { - Optional> pendingResult = executor.getPendingResult(scenarioName); - Future scenarioResultFuture = pendingResult.get(); + Optional> pendingResult = executor.getPendingResult(scenarioName); + Future scenarioResultFuture = pendingResult.get(); return new LiveScenarioView(pendingScenario.get()); } else { throw new RuntimeException("Scenario name '" + scenarioName + "' not found."); diff --git a/engine-rest/src/main/java/io/nosqlbench/engine/rest/transfertypes/ResultView.java b/engine-rest/src/main/java/io/nosqlbench/engine/rest/transfertypes/ResultView.java index e84da8113..9bf85980f 100644 --- a/engine-rest/src/main/java/io/nosqlbench/engine/rest/transfertypes/ResultView.java +++ b/engine-rest/src/main/java/io/nosqlbench/engine/rest/transfertypes/ResultView.java @@ -16,18 +16,18 @@ package io.nosqlbench.engine.rest.transfertypes; -import io.nosqlbench.engine.core.lifecycle.ExecMetricsResult; +import io.nosqlbench.engine.core.lifecycle.ExecutionMetricsResult; public class ResultView { - private final ExecMetricsResult result; + private final ExecutionMetricsResult result; - public ResultView(ExecMetricsResult result) { + public ResultView(ExecutionMetricsResult result) { this.result = result; } public String getIOLog() { - if (result!=null) { + if (result != null) { return result.getIOLog(); } else { return ""; @@ -35,8 +35,8 @@ public class ResultView { } public String getError() { - if (result!=null && result.getException().isPresent()) { - return result.getException().get().getMessage(); + if (result != null && result.getException()!=null) { + return result.getException().getMessage(); } return ""; } diff --git a/nbr-examples/src/test/java/io/nosqlbench/nbr/examples/ScriptExampleTests.java b/nbr-examples/src/test/java/io/nosqlbench/nbr/examples/ScriptExampleTests.java index a84a712b2..28988115e 100644 --- a/nbr-examples/src/test/java/io/nosqlbench/nbr/examples/ScriptExampleTests.java +++ b/nbr-examples/src/test/java/io/nosqlbench/nbr/examples/ScriptExampleTests.java @@ -16,10 +16,10 @@ package io.nosqlbench.nbr.examples; -import io.nosqlbench.engine.core.lifecycle.ExecMetricsResult; -import io.nosqlbench.engine.core.lifecycle.ScenariosResults; -import io.nosqlbench.engine.core.script.Scenario; -import io.nosqlbench.engine.core.script.ScenariosExecutor; +import io.nosqlbench.engine.core.lifecycle.ExecutionMetricsResult; +import io.nosqlbench.engine.core.lifecycle.scenario.ScenariosResults; +import io.nosqlbench.engine.core.lifecycle.scenario.Scenario; +import io.nosqlbench.engine.core.lifecycle.scenario.ScenariosExecutor; import io.nosqlbench.nb.annotations.Maturity; import org.apache.commons.compress.utils.IOUtils; import org.assertj.core.data.Offset; @@ -41,7 +41,7 @@ import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; public class ScriptExampleTests { - public static ExecMetricsResult runScenario(String scriptname, String... params) { + public static ExecutionMetricsResult runScenario(String scriptname, String... params) { if ((params.length % 2) != 0) { throw new RuntimeException("params must be pairwise key, value, ..."); } @@ -74,7 +74,7 @@ public class ScriptExampleTests { // s.addScriptText("load('classpath:scripts/async/" + scriptname + ".js');"); executor.execute(s); ScenariosResults scenariosResults = executor.awaitAllResults(); - ExecMetricsResult scenarioResult = scenariosResults.getOne(); + ExecutionMetricsResult scenarioResult = scenariosResults.getOne(); executor.shutdownNow(); return scenarioResult; } @@ -86,7 +86,7 @@ public class ScriptExampleTests { @Test public void testLinkedInput() { - ExecMetricsResult scenarioResult = runScenario("linkedinput"); + ExecutionMetricsResult scenarioResult = runScenario("linkedinput"); Pattern p = Pattern.compile(".*started leader.*started follower.*stopped leader.*stopped follower.*", Pattern.DOTALL); assertThat(p.matcher(scenarioResult.getIOLog()).matches()).isTrue(); @@ -94,14 +94,14 @@ public class ScriptExampleTests { @Test public void testExceptionPropagationFromMotorThread() { - ExecMetricsResult scenarioResult = runScenario("activityerror"); - assertThat(scenarioResult.getException()).isPresent(); - assertThat(scenarioResult.getException().get().getMessage()).contains("For input string: \"unparsable\""); + ExecutionMetricsResult scenarioResult = runScenario("activityerror"); + assertThat(scenarioResult.getException()).isNotNull(); + assertThat(scenarioResult.getException().getMessage()).contains("For input string: \"unparsable\""); } @Test public void testCycleRate() { - ExecMetricsResult scenarioResult = runScenario("cycle_rate"); + ExecutionMetricsResult scenarioResult = runScenario("cycle_rate"); String iolog = scenarioResult.getIOLog(); System.out.println("iolog\n" + iolog); Pattern p = Pattern.compile(".*mean cycle rate = (\\d[.\\d]+).*", Pattern.DOTALL); @@ -116,13 +116,13 @@ public class ScriptExampleTests { @Test public void testExtensionPoint() { - ExecMetricsResult scenarioResult = runScenario("extensions"); + ExecutionMetricsResult scenarioResult = runScenario("extensions"); assertThat(scenarioResult.getIOLog()).contains("sum is 46"); } @Test public void testOptimo() { - ExecMetricsResult scenarioResult = runScenario("optimo"); + ExecutionMetricsResult scenarioResult = runScenario("optimo"); String iolog = scenarioResult.getIOLog(); System.out.println("iolog\n" + iolog); assertThat(iolog).contains("map of result was"); @@ -130,14 +130,14 @@ public class ScriptExampleTests { @Test public void testExtensionCsvLogger() { - ExecMetricsResult scenarioResult = runScenario("extension_csvmetrics"); + ExecutionMetricsResult scenarioResult = runScenario("extension_csvmetrics"); assertThat(scenarioResult.getIOLog()).contains("started new " + "csvlogger: logs/csvmetricstestdir"); } @Test public void testScriptParamsVariable() { - ExecMetricsResult scenarioResult = runScenario("params_variable", "one", "two", "three", "four"); + ExecutionMetricsResult scenarioResult = runScenario("params_variable", "one", "two", "three", "four"); assertThat(scenarioResult.getIOLog()).contains("params[\"one\"]='two'"); assertThat(scenarioResult.getIOLog()).contains("params[\"three\"]='four'"); assertThat(scenarioResult.getIOLog()).contains("overridden[\"three\"] [overridden-three-five]='five'"); @@ -146,7 +146,7 @@ public class ScriptExampleTests { @Test public void testScriptParamsUndefVariableWithOverride() { - ExecMetricsResult scenarioResult = runScenario("undef_param", "one", "two", "three", "four"); + ExecutionMetricsResult scenarioResult = runScenario("undef_param", "one", "two", "three", "four"); assertThat(scenarioResult.getIOLog()).contains("before: params[\"three\"]:four"); assertThat(scenarioResult.getIOLog()).contains("before: params.three:four"); assertThat(scenarioResult.getIOLog()).contains("after: params[\"three\"]:undefined"); @@ -155,7 +155,7 @@ public class ScriptExampleTests { @Test public void testExtensionHistoStatsLogger() throws IOException { - ExecMetricsResult scenarioResult = runScenario("extension_histostatslogger"); + ExecutionMetricsResult scenarioResult = runScenario("extension_histostatslogger"); assertThat(scenarioResult.getIOLog()).contains("stdout started " + "logging to logs/histostats.csv"); List strings = Files.readAllLines(Paths.get( @@ -167,7 +167,7 @@ public class ScriptExampleTests { @Test public void testExtensionCsvOutput() throws IOException { - ExecMetricsResult scenarioResult = runScenario("extension_csvoutput"); + ExecutionMetricsResult scenarioResult = runScenario("extension_csvoutput"); List strings = Files.readAllLines(Paths.get( "logs/csvoutputtestfile.csv")); String logdata = strings.stream().collect(Collectors.joining("\n")); @@ -177,7 +177,7 @@ public class ScriptExampleTests { @Test public void testExtensionHistogramLogger() throws IOException { - ExecMetricsResult scenarioResult = runScenario("extension_histologger"); + ExecutionMetricsResult scenarioResult = runScenario("extension_histologger"); assertThat(scenarioResult.getIOLog()).contains("stdout started logging to hdrhistodata.log"); List strings = Files.readAllLines(Paths.get("hdrhistodata.log")); String logdata = strings.stream().collect(Collectors.joining("\n")); @@ -187,7 +187,7 @@ public class ScriptExampleTests { @Test public void testBlockingRun() { - ExecMetricsResult scenarioResult = runScenario("blockingrun"); + ExecutionMetricsResult scenarioResult = runScenario("blockingrun"); int a1end = scenarioResult.getIOLog().indexOf("blockingactivity1 finished"); int a2start = scenarioResult.getIOLog().indexOf("running blockingactivity2"); assertThat(a1end).isLessThan(a2start); @@ -195,12 +195,12 @@ public class ScriptExampleTests { @Test public void testAwaitFinished() { - ExecMetricsResult scenarioResult = runScenario("awaitfinished"); + ExecutionMetricsResult scenarioResult = runScenario("awaitfinished"); } @Test public void testStartStop() { - ExecMetricsResult scenarioResult = runScenario("startstopdiag"); + ExecutionMetricsResult scenarioResult = runScenario("startstopdiag"); int startedAt = scenarioResult.getIOLog().indexOf("starting activity teststartstopdiag"); int stoppedAt = scenarioResult.getIOLog().indexOf("stopped activity teststartstopdiag"); assertThat(startedAt).isGreaterThan(0); @@ -210,7 +210,7 @@ public class ScriptExampleTests { // TODO: find out why this causes a long delay after stop is called. @Test public void testThreadChange() { - ExecMetricsResult scenarioResult = runScenario("threadchange"); + ExecutionMetricsResult scenarioResult = runScenario("threadchange"); int changedTo1At = scenarioResult.getIOLog().indexOf("threads now 1"); int changedTo5At = scenarioResult.getIOLog().indexOf("threads now 5"); System.out.println("IOLOG:\n"+scenarioResult.getIOLog()); @@ -220,13 +220,13 @@ public class ScriptExampleTests { @Test public void testReadMetric() { - ExecMetricsResult scenarioResult = runScenario("readmetrics"); + ExecutionMetricsResult scenarioResult = runScenario("readmetrics"); assertThat(scenarioResult.getIOLog()).contains("count: "); } @Test public void testShutdownHook() { - ExecMetricsResult scenarioResult = runScenario("extension_shutdown_hook"); + ExecutionMetricsResult scenarioResult = runScenario("extension_shutdown_hook"); assertThat(scenarioResult.getIOLog()).doesNotContain("shutdown hook running").describedAs( "shutdown hooks should not run in the same IO context as the main scenario" ); @@ -234,15 +234,15 @@ public class ScriptExampleTests { @Test public void testExceptionPropagationFromActivityInit() { - ExecMetricsResult scenarioResult = runScenario("activityiniterror"); - assertThat(scenarioResult.getException()).isPresent(); - assertThat(scenarioResult.getException().get().getMessage()).contains("Unable to convert end cycle from invalid"); + ExecutionMetricsResult scenarioResult = runScenario("activityiniterror"); + assertThat(scenarioResult.getException()).isNotNull(); + assertThat(scenarioResult.getException().getMessage()).contains("Unable to convert end cycle from invalid"); assertThat(scenarioResult.getException()).isNotNull(); } @Test public void testReportedCoDelayBursty() { - ExecMetricsResult scenarioResult = runScenario("cocycledelay_bursty"); + ExecutionMetricsResult scenarioResult = runScenario("cocycledelay_bursty"); assertThat(scenarioResult.getIOLog()).contains("step1 metrics.waittime="); assertThat(scenarioResult.getIOLog()).contains("step2 metrics.waittime="); String iolog = scenarioResult.getIOLog(); @@ -252,7 +252,7 @@ public class ScriptExampleTests { @Test public void testReportedCoDelayStrict() { - ExecMetricsResult scenarioResult = runScenario("cocycledelay_strict"); + ExecutionMetricsResult scenarioResult = runScenario("cocycledelay_strict"); assertThat(scenarioResult.getIOLog()).contains("step1 cycles.waittime="); assertThat(scenarioResult.getIOLog()).contains("step2 cycles.waittime="); String iolog = scenarioResult.getIOLog(); @@ -263,14 +263,14 @@ public class ScriptExampleTests { @Test public void testCycleRateChangeNewMetrics() { - ExecMetricsResult scenarioResult = runScenario("cycle_rate_change"); + ExecutionMetricsResult scenarioResult = runScenario("cycle_rate_change"); String ioLog = scenarioResult.getIOLog(); assertThat(ioLog).contains("cycles adjusted, exiting on iteration"); } @Test public void testExitLogic() { - ExecMetricsResult scenarioResult = runScenario( + ExecutionMetricsResult scenarioResult = runScenario( "basicdiag", "type", "diag", "cyclerate", "5", "erroroncycle", "10", "cycles", "2000" ); diff --git a/nbr-examples/src/test/java/io/nosqlbench/nbr/examples/SpeedCheckIntegrationTests.java b/nbr-examples/src/test/java/io/nosqlbench/nbr/examples/SpeedCheckIntegrationTests.java index b750530d6..f4b230c55 100644 --- a/nbr-examples/src/test/java/io/nosqlbench/nbr/examples/SpeedCheckIntegrationTests.java +++ b/nbr-examples/src/test/java/io/nosqlbench/nbr/examples/SpeedCheckIntegrationTests.java @@ -15,7 +15,7 @@ */ package io.nosqlbench.nbr.examples; -import io.nosqlbench.engine.core.lifecycle.ExecMetricsResult; +import io.nosqlbench.engine.core.lifecycle.ExecutionMetricsResult; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -30,14 +30,14 @@ public class SpeedCheckIntegrationTests { @Disabled // Verified as working public void testSpeedSanity() { - ExecMetricsResult scenarioResult = ScriptExampleTests.runScenario("speedcheck"); + ExecutionMetricsResult scenarioResult = ScriptExampleTests.runScenario("speedcheck"); } @Test @Disabled // This seems incomplete public void testThreadSpeeds() { - ExecMetricsResult scenarioResult = ScriptExampleTests.runScenario("threadspeeds"); + ExecutionMetricsResult scenarioResult = ScriptExampleTests.runScenario("threadspeeds"); } From 0de80887b1c306a7df4f375263cd0e79b1a8d94b Mon Sep 17 00:00:00 2001 From: Jonathan Shook Date: Tue, 20 Dec 2022 20:03:33 -0600 Subject: [PATCH 12/19] move log4j.xml to log4j2.xml --- .../{log4j-test.xml => log4j2-test.xml} | 20 ++++++- .../main/resources/{log4j.xml => log4j2.xml} | 20 ++++++- .../{log4j-test.xml => log4j2-test.xml} | 20 ++++++- .../main/resources/{log4j.xml => log4j2.xml} | 20 ++++++- engine-core/src/test/resources/log4j-test.xml | 41 ------------- .../src/test/resources/log4j2-test.xml | 57 +++++++++++++++++++ .../src/test/resources/log4j2-test.xml | 57 +++++++++++++++++++ nbr/src/main/resources/log4j.xml | 41 ------------- nbr/src/main/resources/log4j2.xml | 57 +++++++++++++++++++ .../src/test/resources/log4j-test.xml | 41 ------------- .../src/test/resources/log4j2-test.xml | 57 +++++++++++++++++++ .../src/test/resources/log4j-test.xml | 41 ------------- .../src/test/resources/log4j2-test.xml | 57 +++++++++++++++++++ .../src/main/resources/log4j.xml | 41 ------------- .../src/main/resources/log4j2.xml | 57 +++++++++++++++++++ .../src/test/resources/log4j-test.xml | 41 ------------- .../src/test/resources/log4j2-test.xml | 57 +++++++++++++++++++ 17 files changed, 471 insertions(+), 254 deletions(-) rename engine-api/src/test/resources/{log4j-test.xml => log4j2-test.xml} (68%) rename engine-cli/src/main/resources/{log4j.xml => log4j2.xml} (68%) rename engine-cli/src/test/resources/{log4j-test.xml => log4j2-test.xml} (68%) rename engine-core/src/main/resources/{log4j.xml => log4j2.xml} (68%) delete mode 100644 engine-core/src/test/resources/log4j-test.xml create mode 100644 engine-core/src/test/resources/log4j2-test.xml create mode 100644 engine-rest/src/test/resources/log4j2-test.xml delete mode 100644 nbr/src/main/resources/log4j.xml create mode 100644 nbr/src/main/resources/log4j2.xml delete mode 100644 virtdata-lang/src/test/resources/log4j-test.xml create mode 100644 virtdata-lang/src/test/resources/log4j2-test.xml delete mode 100644 virtdata-lib-basics/src/test/resources/log4j-test.xml create mode 100644 virtdata-lib-basics/src/test/resources/log4j2-test.xml delete mode 100644 virtdata-userlibs/src/main/resources/log4j.xml create mode 100644 virtdata-userlibs/src/main/resources/log4j2.xml delete mode 100644 virtdata-userlibs/src/test/resources/log4j-test.xml create mode 100644 virtdata-userlibs/src/test/resources/log4j2-test.xml diff --git a/engine-api/src/test/resources/log4j-test.xml b/engine-api/src/test/resources/log4j2-test.xml similarity index 68% rename from engine-api/src/test/resources/log4j-test.xml rename to engine-api/src/test/resources/log4j2-test.xml index 70176aaf6..797a5780b 100644 --- a/engine-api/src/test/resources/log4j-test.xml +++ b/engine-api/src/test/resources/log4j2-test.xml @@ -1,4 +1,20 @@ + + @@ -28,7 +44,7 @@ - + @@ -38,4 +54,4 @@ - \ No newline at end of file + diff --git a/engine-cli/src/main/resources/log4j.xml b/engine-cli/src/main/resources/log4j2.xml similarity index 68% rename from engine-cli/src/main/resources/log4j.xml rename to engine-cli/src/main/resources/log4j2.xml index 70176aaf6..797a5780b 100644 --- a/engine-cli/src/main/resources/log4j.xml +++ b/engine-cli/src/main/resources/log4j2.xml @@ -1,4 +1,20 @@ + + @@ -28,7 +44,7 @@ - + @@ -38,4 +54,4 @@ - \ No newline at end of file + diff --git a/engine-cli/src/test/resources/log4j-test.xml b/engine-cli/src/test/resources/log4j2-test.xml similarity index 68% rename from engine-cli/src/test/resources/log4j-test.xml rename to engine-cli/src/test/resources/log4j2-test.xml index 70176aaf6..797a5780b 100644 --- a/engine-cli/src/test/resources/log4j-test.xml +++ b/engine-cli/src/test/resources/log4j2-test.xml @@ -1,4 +1,20 @@ + + @@ -28,7 +44,7 @@ - + @@ -38,4 +54,4 @@ - \ No newline at end of file + diff --git a/engine-core/src/main/resources/log4j.xml b/engine-core/src/main/resources/log4j2.xml similarity index 68% rename from engine-core/src/main/resources/log4j.xml rename to engine-core/src/main/resources/log4j2.xml index 70176aaf6..797a5780b 100644 --- a/engine-core/src/main/resources/log4j.xml +++ b/engine-core/src/main/resources/log4j2.xml @@ -1,4 +1,20 @@ + + @@ -28,7 +44,7 @@ - + @@ -38,4 +54,4 @@ - \ No newline at end of file + diff --git a/engine-core/src/test/resources/log4j-test.xml b/engine-core/src/test/resources/log4j-test.xml deleted file mode 100644 index 70176aaf6..000000000 --- a/engine-core/src/test/resources/log4j-test.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - %d %p %C{1.} [%t] %m%n - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/engine-core/src/test/resources/log4j2-test.xml b/engine-core/src/test/resources/log4j2-test.xml new file mode 100644 index 000000000..797a5780b --- /dev/null +++ b/engine-core/src/test/resources/log4j2-test.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + %d %p %C{1.} [%t] %m%n + + + + + + + + + + + + + + + + + diff --git a/engine-rest/src/test/resources/log4j2-test.xml b/engine-rest/src/test/resources/log4j2-test.xml new file mode 100644 index 000000000..3ee115d07 --- /dev/null +++ b/engine-rest/src/test/resources/log4j2-test.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + %d %p %C{1.} [%t] %m%n + + + + + + + + + + + + + + + + + diff --git a/nbr/src/main/resources/log4j.xml b/nbr/src/main/resources/log4j.xml deleted file mode 100644 index 70176aaf6..000000000 --- a/nbr/src/main/resources/log4j.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - %d %p %C{1.} [%t] %m%n - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/nbr/src/main/resources/log4j2.xml b/nbr/src/main/resources/log4j2.xml new file mode 100644 index 000000000..797a5780b --- /dev/null +++ b/nbr/src/main/resources/log4j2.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + %d %p %C{1.} [%t] %m%n + + + + + + + + + + + + + + + + + diff --git a/virtdata-lang/src/test/resources/log4j-test.xml b/virtdata-lang/src/test/resources/log4j-test.xml deleted file mode 100644 index 70176aaf6..000000000 --- a/virtdata-lang/src/test/resources/log4j-test.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - %d %p %C{1.} [%t] %m%n - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/virtdata-lang/src/test/resources/log4j2-test.xml b/virtdata-lang/src/test/resources/log4j2-test.xml new file mode 100644 index 000000000..797a5780b --- /dev/null +++ b/virtdata-lang/src/test/resources/log4j2-test.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + %d %p %C{1.} [%t] %m%n + + + + + + + + + + + + + + + + + diff --git a/virtdata-lib-basics/src/test/resources/log4j-test.xml b/virtdata-lib-basics/src/test/resources/log4j-test.xml deleted file mode 100644 index 70176aaf6..000000000 --- a/virtdata-lib-basics/src/test/resources/log4j-test.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - %d %p %C{1.} [%t] %m%n - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/virtdata-lib-basics/src/test/resources/log4j2-test.xml b/virtdata-lib-basics/src/test/resources/log4j2-test.xml new file mode 100644 index 000000000..797a5780b --- /dev/null +++ b/virtdata-lib-basics/src/test/resources/log4j2-test.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + %d %p %C{1.} [%t] %m%n + + + + + + + + + + + + + + + + + diff --git a/virtdata-userlibs/src/main/resources/log4j.xml b/virtdata-userlibs/src/main/resources/log4j.xml deleted file mode 100644 index 70176aaf6..000000000 --- a/virtdata-userlibs/src/main/resources/log4j.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - %d %p %C{1.} [%t] %m%n - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/virtdata-userlibs/src/main/resources/log4j2.xml b/virtdata-userlibs/src/main/resources/log4j2.xml new file mode 100644 index 000000000..797a5780b --- /dev/null +++ b/virtdata-userlibs/src/main/resources/log4j2.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + %d %p %C{1.} [%t] %m%n + + + + + + + + + + + + + + + + + diff --git a/virtdata-userlibs/src/test/resources/log4j-test.xml b/virtdata-userlibs/src/test/resources/log4j-test.xml deleted file mode 100644 index 70176aaf6..000000000 --- a/virtdata-userlibs/src/test/resources/log4j-test.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - %d %p %C{1.} [%t] %m%n - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/virtdata-userlibs/src/test/resources/log4j2-test.xml b/virtdata-userlibs/src/test/resources/log4j2-test.xml new file mode 100644 index 000000000..797a5780b --- /dev/null +++ b/virtdata-userlibs/src/test/resources/log4j2-test.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + %d %p %C{1.} [%t] %m%n + + + + + + + + + + + + + + + + + From bf5a31b342a5b944d06f42625abdc4abbccfff3a Mon Sep 17 00:00:00 2001 From: Jonathan Shook Date: Tue, 20 Dec 2022 20:04:12 -0600 Subject: [PATCH 13/19] implement efficient concurrent thread state signaling --- .../engine/api/activityapi/core/Activity.java | 3 + .../engine/api/activityapi/core/RunState.java | 63 +++-- ...{SlotStateTracker.java => MotorState.java} | 32 ++- .../api/activityimpl/SimpleActivity.java | 17 +- .../api/activityimpl/motor/RunStateImage.java | 71 +++++ .../api/activityimpl/motor/RunStateTally.java | 266 ++++++++++++++++++ .../activityimpl/motor/RunStateImageTest.java | 39 +++ .../activityimpl/motor/RunStateTallyTest.java | 214 ++++++++++++++ .../nosqlbench/engine/core/CoreMotorTest.java | 2 - 9 files changed, 672 insertions(+), 35 deletions(-) rename engine-api/src/main/java/io/nosqlbench/engine/api/activityimpl/{SlotStateTracker.java => MotorState.java} (70%) create mode 100644 engine-api/src/main/java/io/nosqlbench/engine/api/activityimpl/motor/RunStateImage.java create mode 100644 engine-api/src/main/java/io/nosqlbench/engine/api/activityimpl/motor/RunStateTally.java create mode 100644 engine-api/src/test/java/io/nosqlbench/engine/api/activityimpl/motor/RunStateImageTest.java create mode 100644 engine-api/src/test/java/io/nosqlbench/engine/api/activityimpl/motor/RunStateTallyTest.java diff --git a/engine-api/src/main/java/io/nosqlbench/engine/api/activityapi/core/Activity.java b/engine-api/src/main/java/io/nosqlbench/engine/api/activityapi/core/Activity.java index 618def3db..cab8f0999 100644 --- a/engine-api/src/main/java/io/nosqlbench/engine/api/activityapi/core/Activity.java +++ b/engine-api/src/main/java/io/nosqlbench/engine/api/activityapi/core/Activity.java @@ -28,6 +28,7 @@ import io.nosqlbench.engine.api.activityapi.ratelimits.RateLimiter; import io.nosqlbench.api.engine.activityimpl.ActivityDef; import io.nosqlbench.api.engine.activityimpl.ParameterMap; import io.nosqlbench.engine.api.activityimpl.SimpleActivity; +import io.nosqlbench.engine.api.activityimpl.motor.RunStateTally; import java.io.InputStream; import java.io.PrintWriter; @@ -215,4 +216,6 @@ public interface Activity extends Comparable, ActivityDefObserver, Pro default int getHdrDigits() { return getParams().getOptionalInteger("hdr_digits").orElse(4); } + + RunStateTally getRunStateTally(); } diff --git a/engine-api/src/main/java/io/nosqlbench/engine/api/activityapi/core/RunState.java b/engine-api/src/main/java/io/nosqlbench/engine/api/activityapi/core/RunState.java index 15ca92a96..0476dc762 100644 --- a/engine-api/src/main/java/io/nosqlbench/engine/api/activityapi/core/RunState.java +++ b/engine-api/src/main/java/io/nosqlbench/engine/api/activityapi/core/RunState.java @@ -16,39 +16,69 @@ package io.nosqlbench.engine.api.activityapi.core; +import io.nosqlbench.engine.api.activityimpl.MotorState; + +/** + *

This enum indicates the state of a thread within an activity. The state is kept in an atomic + * register. Ownership of state changes is shared between a supervising thread and a managed thread. + * Both can make changes to the state.

+ * + *

These states are ordered such that the highest ordinal state represents the most significant + * aggregate state of all motors. That is, if any has errored, then the other states do not matter. + * If any is finished, then stopped motors don't matter, and so on. This makes summarizing aggregate + * state simply a matter of ordering.

+ * + */ public enum RunState { - /** - * Initial state after creation of this control + * 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⌀"), /** - * This thread has been queued to run, but hasn't signaled yet that it is full started - * This must be set by the executor before executing the slot runnable + * A thread has been invoked, but is initializing and preparing for its main control loop. + * This is signaled by the motor after {@link Runnable#run}, but before entering the main processing + * loop. */ Starting("s⏫"), /** - * This thread is running. This should only be set by the controlled thread + * A thread is iterating within the main control loop. + * This is signaled by the motor once initialization in the main loop is complete and immediately + * before it enters it's main processing loop. */ Running("R\u23F5"), /** - * This thread has completed all of its activity, and will do no further work without new input - */ - Finished("F⏯"), - - /** - * The thread has been requested to stop. This says nothing of the internal state. + *

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.

*/ Stopping("s⏬"), /** - * The thread has stopped. This should only be set by the controlled thread + * The thread has stopped. This should only be set by the motor. This state will only be visible + * to signaling mechanisms so long as the motor is still managed. + * + *

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.

*/ - Stopped("_\u23F9"); + Stopped("e\u23F9"), + + /** + *

A thread has exhausted its supply of values on the input (AKA cycles), thus has completed its work. + * This is signaled upon a short read of the input by the motor.

+ * + *

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.

+ */ + Finished("F⏯"), + + /** + * If a motor has seen an exception, it goes into errored state before propagating the error. + */ + Errored("E⚠"); private final String runcode; @@ -69,15 +99,16 @@ public enum RunState { default -> false; // A motor was just created. This is its initial state. case Uninitialized, Stopped -> (target == Starting); case Starting -> switch (target) { // a motor has indicated that is in the run() method - case Running, Finished -> true;// a motor has exhausted its input, and has declined to go into started mode + case Running, Finished, Errored -> true;// a motor has exhausted its input, and has declined to go into started mode default -> false; }; case Running -> switch (target) { // A request was made to stop the motor before it finished - case Stopping, Finished -> true;// A motor has exhausted its input, and is finished with its work + case Stopping, Finished, Errored -> true;// A motor has exhausted its input, and is finished with its work default -> false; }; - case Stopping -> (target == Stopped); // A motor was stopped by request before exhausting input + case Stopping -> (target == Stopped||target==Finished); // A motor was stopped by request before exhausting input case Finished -> (target == Running); // A motor was restarted? + case Errored -> target==Errored; }; } diff --git a/engine-api/src/main/java/io/nosqlbench/engine/api/activityimpl/SlotStateTracker.java b/engine-api/src/main/java/io/nosqlbench/engine/api/activityimpl/MotorState.java similarity index 70% rename from engine-api/src/main/java/io/nosqlbench/engine/api/activityimpl/SlotStateTracker.java rename to engine-api/src/main/java/io/nosqlbench/engine/api/activityimpl/MotorState.java index 5d164333e..0dfbfe2c6 100644 --- a/engine-api/src/main/java/io/nosqlbench/engine/api/activityimpl/SlotStateTracker.java +++ b/engine-api/src/main/java/io/nosqlbench/engine/api/activityimpl/MotorState.java @@ -17,26 +17,31 @@ package io.nosqlbench.engine.api.activityimpl; import io.nosqlbench.engine.api.activityapi.core.RunState; -import org.apache.logging.log4j.Logger; +import io.nosqlbench.engine.api.activityimpl.motor.RunStateTally; import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Supplier; /** * Holds the state of a slot, allows only valid transitions, and shares the * slot state as */ -public class SlotStateTracker { - private final AtomicReference slotState = new AtomicReference<>(RunState.Uninitialized); - private final static Logger logger = LogManager.getLogger(SlotStateTracker.class); +public class MotorState implements Supplier { + private final static Logger logger = LogManager.getLogger("MOTORS"); + private final AtomicReference atomicState = new AtomicReference<>(RunState.Uninitialized); private final long slotId; + private final RunStateTally tally; - public SlotStateTracker(long slotId) { + public MotorState(long slotId, RunStateTally tally) { this.slotId = slotId; + this.tally = tally; + tally.add(atomicState.get()); } - public RunState getSlotState() { - return slotState.get(); + public RunState get() { + return atomicState.get(); } /** @@ -46,7 +51,7 @@ public class SlotStateTracker { * @return an atomic reference for SlotState */ public AtomicReference getAtomicSlotState() { - return slotState; + return atomicState; } /** @@ -56,16 +61,19 @@ public class SlotStateTracker { * @param to The next SlotState for this thread/slot/motor */ public synchronized void enterState(RunState to) { - RunState from = slotState.get(); + RunState from = atomicState.get(); if (!from.canTransitionTo(to)) { throw new RuntimeException("Invalid transition from " + from + " to " + to); } - while (!slotState.compareAndSet(from, to)) { + while (!atomicState.compareAndSet(from, to)) { logger.trace("retrying transition from:" + from + " to:" + to); } + tally.change(from,to); logger.trace("TRANSITION[" + slotId + "]: " + from + " ==> " + to); - } - + public void removeState() { + logger.trace(() -> "Removing motor state " + atomicState.get()); + tally.remove(atomicState.get()); + } } diff --git a/engine-api/src/main/java/io/nosqlbench/engine/api/activityimpl/SimpleActivity.java b/engine-api/src/main/java/io/nosqlbench/engine/api/activityimpl/SimpleActivity.java index dec179776..a988d3634 100644 --- a/engine-api/src/main/java/io/nosqlbench/engine/api/activityimpl/SimpleActivity.java +++ b/engine-api/src/main/java/io/nosqlbench/engine/api/activityimpl/SimpleActivity.java @@ -36,6 +36,7 @@ import io.nosqlbench.engine.api.activityapi.ratelimits.RateSpec; import io.nosqlbench.engine.api.activityconfig.StatementsLoader; import io.nosqlbench.engine.api.activityconfig.yaml.OpTemplate; import io.nosqlbench.engine.api.activityconfig.yaml.StmtsDocList; +import io.nosqlbench.engine.api.activityimpl.motor.RunStateTally; import io.nosqlbench.engine.api.activityimpl.uniform.DriverAdapter; import io.nosqlbench.engine.api.activityimpl.uniform.decorators.SyntheticOpTemplateProvider; import io.nosqlbench.engine.api.activityimpl.uniform.flowtypes.Op; @@ -82,6 +83,7 @@ public class SimpleActivity implements Activity, ProgressCapable, ActivityDefObs private NBErrorHandler errorHandler; private ActivityMetricProgressMeter progressMeter; private String workloadSource = "unspecified"; + private final RunStateTally tally = new RunStateTally(); public SimpleActivity(ActivityDef activityDef) { this.activityDef = activityDef; @@ -95,7 +97,7 @@ public class SimpleActivity implements Activity, ProgressCapable, ActivityDefObs } else { activityDef.getParams().set("alias", activityDef.getActivityType().toUpperCase(Locale.ROOT) - + String.valueOf(nameEnumerator++)); + + nameEnumerator++); } } } @@ -190,7 +192,7 @@ public class SimpleActivity implements Activity, ProgressCapable, ActivityDefObs } public String toString() { - return getAlias(); + return getAlias()+":"+getRunState()+":"+getRunStateTally().toString(); } @Override @@ -217,7 +219,7 @@ public class SimpleActivity implements Activity, ProgressCapable, ActivityDefObs @Override public void closeAutoCloseables() { for (AutoCloseable closeable : closeables) { - logger.debug("CLOSING " + closeable.getClass().getCanonicalName() + ": " + closeable.toString()); + logger.debug("CLOSING " + closeable.getClass().getCanonicalName() + ": " + closeable); try { closeable.close(); } catch (Exception e) { @@ -392,7 +394,7 @@ public class SimpleActivity implements Activity, ProgressCapable, ActivityDefObs if (threadSpec.isPresent()) { String spec = threadSpec.get(); int processors = Runtime.getRuntime().availableProcessors(); - if (spec.toLowerCase().equals("auto")) { + if (spec.equalsIgnoreCase("auto")) { int threads = processors * 10; if (threads > activityDef.getCycleCount()) { threads = (int) activityDef.getCycleCount(); @@ -652,7 +654,7 @@ public class SimpleActivity implements Activity, ProgressCapable, ActivityDefObs return stmtsDocList; } catch (Exception e) { - throw new OpConfigError("Error loading op templates: " + e.toString(), workloadSource, e); + throw new OpConfigError("Error loading op templates: " + e, workloadSource, e); } } @@ -677,6 +679,11 @@ public class SimpleActivity implements Activity, ProgressCapable, ActivityDefObs return getActivityDef().getParams().getOptionalInteger("maxtries").orElse(10); } + @Override + public RunStateTally getRunStateTally() { + return tally; + } + @Override public String getName() { diff --git a/engine-api/src/main/java/io/nosqlbench/engine/api/activityimpl/motor/RunStateImage.java b/engine-api/src/main/java/io/nosqlbench/engine/api/activityimpl/motor/RunStateImage.java new file mode 100644 index 000000000..ae95eafaf --- /dev/null +++ b/engine-api/src/main/java/io/nosqlbench/engine/api/activityimpl/motor/RunStateImage.java @@ -0,0 +1,71 @@ +/* + * 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.api.activityimpl.motor; + +import io.nosqlbench.engine.api.activityapi.core.RunState; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +/** + * A value type which encodes the atomic state of a RunState tally. + */ +public class RunStateImage { + private final static Logger logger = LogManager.getLogger("TALLY"); + + private final int[] counts = new int[RunState.values().length]; + private final boolean timedout; + + public RunStateImage(int[] counts, boolean timedout) { + System.arraycopy(counts, 0, this.counts, 0, counts.length); + this.timedout = timedout; + } + + public boolean isTimeout() { + return this.timedout; + } + + public boolean is(RunState runState) { + return counts[runState.ordinal()]>0; + } + + public boolean isOnly(RunState runState) { + for (int i = 0; i < counts.length; i++) { + if (counts[i]>0 && i!=runState.ordinal()) { + return false; + } + } + return true; + } + + public RunState getMaxState() { + for (int ord = counts.length-1; ord >= 0; ord--) { + if (counts[ord]>0) { + return RunState.values()[ord]; + } + } + throw new RuntimeException("There were zero states, so max state is undefined"); + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + for (RunState runState : RunState.values()) { + sb.append(runState.getCode()).append(" ").append(counts[runState.ordinal()]).append(" "); + } + return sb.toString(); + } + +} diff --git a/engine-api/src/main/java/io/nosqlbench/engine/api/activityimpl/motor/RunStateTally.java b/engine-api/src/main/java/io/nosqlbench/engine/api/activityimpl/motor/RunStateTally.java new file mode 100644 index 000000000..dc861f9cc --- /dev/null +++ b/engine-api/src/main/java/io/nosqlbench/engine/api/activityimpl/motor/RunStateTally.java @@ -0,0 +1,266 @@ +/* + * 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.api.activityimpl.motor; + +import io.nosqlbench.engine.api.activityapi.core.RunState; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.Arrays; + +/** + *

Synopsis

+ *

Event-oriented tally of the runtime states of all + * motor threads. This is meant to be used as the definitive + * aggregate scorecard for an activity's thread states.

+ *
+ *

Purpose

+ *

This limits inter-thread signaling requirements by constraining + * the cases for which blockers are notified. This is because the + * meaningful scenarios for which a blocker would want to be notified + * do not include changes of positive values to other positive values. + * This allows an explicit optimization around scenarios for which a + * state count increments to 1 or decrements to 0, as in "some" or "none". + * This is an effective optimization for scenarios with many active + * threads. + *

+ *
+ *

Calling Semantics

+ *

Callers of the await functions will block for the required condition or, + * if specified, the timeout to occur without the condition. + * These callers are unblocked atomically, after any state add, state remove, + * or state change events are fully completed.

+ *

{@link RunStateImage} is returned from blocking methods so that + * callers can know consistently what the current run states were at + * the time their condition was met or timed out. Any callers of such + * methods must check for whether the condition was successfully + * met via {@link RunStateImage#isTimeout()}

+ *
+ *

Invariants

+ *
    + *
  • Under consistent usage patterns, all counts should be zero or positive at all times.
  • + *
  • The current count for each state should be equal to the visible {@link RunState} on each + * motor thread, as all transitions are made through the motor threads directly.
  • + *
+ */ +public class RunStateTally { + private final static Logger logger = LogManager.getLogger("TALLY"); + + /** + * If no timeout is given for any of the await methods, then the default is to wait for + * approximately many eons. Some tests run until told to stop. + */ + public final long DEFAULT_TIMEOUT_MS=Long.MAX_VALUE; + + private final int[] counts = new int[RunState.values().length]; + + /** + * @return the current count for the specified state + * @param runState The {@link RunState} to count + * + */ + public synchronized int tallyFor(RunState runState) { + return counts[runState.ordinal()]; + } + + /** + * Signal that a motor thread has left one {@link RunState} and entered another, + * atomically. After both counts are updated, if any RunState counts changed to or from zero, + * then all potential observers are notified to re-evaluate their await conditions. + * @param from the prior RunState + * @param to the next RunState + */ + public synchronized void change(RunState from, RunState to) { + counts[from.ordinal()]--; + counts[to.ordinal()]++; + logger.trace(() -> this +" -"+from+ String.format(":%04d",counts[from.ordinal()])+ ", +"+to+ String.format(":%04d",counts[to.ordinal()])); + + if (counts[from.ordinal()]==0 || counts[to.ordinal()]==1) { + logger.debug(() -> "NOTIFYing on edge "+ + "from " + from + String.format(":%04d",counts[from.ordinal()]) + + " to " + to + String.format(":%04d",counts[to.ordinal()])); + notifyAll(); + } + } + + /** + * Add a previously untracked motor thread to state tracking with the provided {@link RunState}. + * If the given state's count becomes non-zero, then all potential observers are notified to re-evaluate + * their await conditions. + * @param state The initial tracking state for the related motor thread + */ + public synchronized void add(RunState state) { + counts[state.ordinal()]++; + logger.trace(() -> this +" +"+state+ String.format(":%04d",counts[state.ordinal()])); + if (counts[state.ordinal()]==1) { + logger.debug(() -> "NOTIFYing on ++-SOME edge for " + state + String.format(":%04d",counts[state.ordinal()])); + notifyAll(); + } + } + + /** + * Remove a previously tracked motor thread from state tracking with the provided {@link RunState}. + * If the given state's count becomes zero, then all potential observers are notified to re-evaluate + * their await conditions. + * @param state The final tracking state for the related motor thread + */ + public synchronized void remove(RunState state) { + counts[state.ordinal()]--; + logger.trace(() -> this +" -"+state+ String.format(":%04d",counts[state.ordinal()])); + if (counts[state.ordinal()]==0) { + logger.debug(() -> "NOTIFYing on 00-NONE edge for " + state + String.format(":%04d",counts[state.ordinal()])); + notifyAll(); + } + } + + /** + * Await until all states but the provided {@link RunState}s have zero counts. + * This condition matches if the provided states have zero or positive counts if and only if + * all other states have zero counts. Thus, if there are no positive values at all, this condition will + * still match. + * @param runStates The states which may have zero counts and still match the condition + * @return A {@link RunStateImage}, indicating success or failure, and the view of states at the time of evaluation + */ + public synchronized RunStateImage awaitNoneOther(RunState... runStates) { + return this.awaitNoneOther(DEFAULT_TIMEOUT_MS, runStates); + } + /** + * Exactly like {@link #awaitNoneOther(long, RunState...)}, except that it allows for a timeout, + * after which the method will unblock and signal an error if the await condition is still false. + * @param runStates RunStates which are the only valid states before unblocking + * @return A {@link RunStateImage}, indicating success or failure, and the view of states at the time of evaluation + */ + public synchronized RunStateImage awaitNoneOther(long timeoutMillis, RunState... runStates) { + logger.debug(() -> "☐ Awaiting only " + Arrays.toString(runStates) + " for " + timeoutMillis+"ms"); + long timeoutAt = timeoutAt(timeoutMillis); + + int sum=0; + for (RunState runState: RunState.values()) { + sum+=counts[runState.ordinal()]; + } + for (RunState runState : runStates) { + sum-=counts[runState.ordinal()]; + } + while (sum>0 && System.currentTimeMillis() (timedout ? "✘ TIMED-OUT awaiting only " : "☑ Awaited only " ) + toString(runStates)); + return new RunStateImage(this.counts,timedout); + } + + private long timeoutAt(long timeoutMillis) { + long delayTill= System.currentTimeMillis() + timeoutMillis; + return (delayTill>0) ? delayTill : Long.MAX_VALUE; + } + /** + * Await until there are zero counts for all of the specified {@link RunState}s. + * @param runStates all RunStates which must be zeroed before unblocking + * @return A {@link RunStateImage}, indicating success or failure, and the view of states at the time of evaluation + */ + public synchronized RunStateImage awaitNoneOf(RunState... runStates) { + return this.awaitNoneOf(DEFAULT_TIMEOUT_MS, runStates); + } + /** + * Exactly like {@link #awaitNoneOf(RunState...)}, except that it allows for a timeout, after which the + * method will unblock and signal a timeout if the await condition is still not met. + * @param runStates all RunStates which must be zeroed before unblocking + * @return A {@link RunStateImage}, indicating success or failure, and the view of states at the time of evaluation + */ + public synchronized RunStateImage awaitNoneOf(long timeoutMillis, RunState... runStates) { + logger.debug(() -> "☐ Awaiting none of " + Arrays.toString(runStates)+ " for " + timeoutMillis+"ms"); + long timeoutAt = timeoutAt(timeoutMillis); + + int sum=0; + for (RunState runState : runStates) { + sum+=counts[runState.ordinal()]; + } + while (sum>0 && System.currentTimeMillis() (timedout ? "✘ TIMED-OUT awaiting none of " : "☑ Awaited none of " ) + toString(runStates)); + return new RunStateImage(this.counts,timedout); + } + + /** + * Await until at least one of the provided runStates has a positive value. + * @param runStates RunStates any of which allow unblocking + * @return A {@link RunStateImage}, indicating success or failure, and the view of states at the time of evaluation + */ + public synchronized RunStateImage awaitAny(RunState... runStates) { + return this.awaitAny(DEFAULT_TIMEOUT_MS,runStates); + } + + /** + * Exactly like {@link #awaitAny(RunState...)}, except that it allows for a timeout, after which + * the method will unblock and signal a timeout if the await condition is still not met. + * @param runStates RunStates any of which allow unblocking + * @param timeoutMillis Milliseconds to wait for any of the runstates before giving up + * @return A {@link RunStateImage}, indicating success or failure, and the view of states at the time of evaluation + */ + public synchronized RunStateImage awaitAny(long timeoutMillis, RunState... runStates) { + logger.debug(() -> "☐ Awaiting any " + Arrays.toString(runStates) + " for " + timeoutMillis + "ms"); + long timeoutAt = timeoutAt(timeoutMillis); + + while (System.currentTimeMillis()0) { + logger.debug("☑ Awaited any " + toString(runStates)); + return new RunStateImage(this.counts,false); + } + } + try { + wait(timeoutAt-System.currentTimeMillis()); + } catch (InterruptedException e) { + } + } + logger.debug(() -> "✘ TIMED-OUT awaiting any of " + toString(runStates)); + return new RunStateImage(this.counts,true); + } + + public String toString(RunState... runStates) { + StringBuilder sb = new StringBuilder(); + for (RunState runState : runStates) { + sb.append(runState.getCode()).append("(").append(counts[runState.ordinal()]).append(") "); + } + sb.setLength(sb.length()-1); + return sb.toString(); + } + public String toString() { + return toString(RunState.values()); + } +} diff --git a/engine-api/src/test/java/io/nosqlbench/engine/api/activityimpl/motor/RunStateImageTest.java b/engine-api/src/test/java/io/nosqlbench/engine/api/activityimpl/motor/RunStateImageTest.java new file mode 100644 index 000000000..d457225ca --- /dev/null +++ b/engine-api/src/test/java/io/nosqlbench/engine/api/activityimpl/motor/RunStateImageTest.java @@ -0,0 +1,39 @@ +/* + * 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.api.activityimpl.motor; + +import io.nosqlbench.engine.api.activityapi.core.RunState; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class RunStateImageTest { + + @Test + public void testMaxStateImage() { + int[] counts = new int[RunState.values().length]; + counts[RunState.Running.ordinal()]=3; + RunStateImage image = new RunStateImage(counts, false); + assertThat(image.is(RunState.Running)).isTrue(); + assertThat(image.isTimeout()).isFalse(); + assertThat(image.is(RunState.Errored)).isFalse(); + assertThat(image.isOnly(RunState.Running)).isTrue(); + RunState maxState = image.getMaxState(); + assertThat(maxState).isEqualTo(RunState.values()[2]); + } + +} diff --git a/engine-api/src/test/java/io/nosqlbench/engine/api/activityimpl/motor/RunStateTallyTest.java b/engine-api/src/test/java/io/nosqlbench/engine/api/activityimpl/motor/RunStateTallyTest.java new file mode 100644 index 000000000..ffb545158 --- /dev/null +++ b/engine-api/src/test/java/io/nosqlbench/engine/api/activityimpl/motor/RunStateTallyTest.java @@ -0,0 +1,214 @@ +/* + * 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.api.activityimpl.motor; + +import io.nosqlbench.engine.api.activityapi.core.RunState; +import org.junit.jupiter.api.*; + +import static org.assertj.core.api.Assertions.assertThat; + +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class RunStateTallyTest { + + volatile boolean awaited = false; + volatile RunStateImage event = null; + + @BeforeEach + public void setup() { + awaited = false; + event = null; + } + + @Test + @Order(1) + public void testAwaitAny() { + Thread.currentThread().setName("SETTER"); + + RunStateTally tally = new RunStateTally(); + awaited = false; + Thread waiter = new Thread(new Runnable() { + @Override + public void run() { + event = tally.awaitAny(RunState.Running); + awaited = true; + } + }); + waiter.setName("WAITER"); + waiter.setDaemon(true); + waiter.start(); + + try { + Thread.sleep(100); + } catch (Exception e) { + } + + assertThat(awaited).isFalse(); + tally.add(RunState.Running); + + try { + Thread.sleep(100); + } catch (Exception e) { + } + + + assertThat(event.is(RunState.Running)).isTrue(); + assertThat(event.isOnly(RunState.Running)).isTrue(); + + assertThat(awaited).isTrue(); + assertThat(waiter.getState()).isNotEqualTo(Thread.State.RUNNABLE); + } + + @Test + @Order(2) + public void testAwaitNoneOf() { + Thread.currentThread().setName("SETTER"); + + RunStateTally tally = new RunStateTally(); + tally.add(RunState.Uninitialized); + tally.add(RunState.Stopped); + awaited = false; + Thread waiter = new Thread(new Runnable() { + @Override + public void run() { + tally.awaitNoneOf(RunState.Stopped, RunState.Uninitialized); + awaited = true; + } + }); + waiter.setName("WAITER"); + waiter.setDaemon(true); + waiter.start(); + + try { + Thread.sleep(100); + } catch (Exception e) { + } + + assertThat(awaited).isFalse(); + tally.change(RunState.Stopped, RunState.Finished); + + try { + Thread.sleep(100); + } catch (Exception e) { + } + + assertThat(awaited).isFalse(); + tally.change(RunState.Uninitialized, RunState.Finished); + + try { + Thread.sleep(100); + } catch (Exception e) { + } + + assertThat(awaited).isTrue(); + assertThat(waiter.getState()).isNotEqualTo(Thread.State.RUNNABLE); + + } + + @Test + @Order(3) + public void testAwaitNoneOther() { + Thread.currentThread().setName("SETTER"); + + RunStateTally tally = new RunStateTally(); + tally.add(RunState.Uninitialized); + tally.add(RunState.Running); + awaited = false; + Thread waiter = new Thread(new Runnable() { + @Override + public void run() { + event = tally.awaitNoneOther(RunState.Stopped, RunState.Finished); + awaited = true; + } + }); + waiter.setName("WAITER"); + waiter.setDaemon(true); + waiter.start(); + + try { + Thread.sleep(100); + } catch (Exception e) { + } + + assertThat(awaited).isFalse(); + tally.change(RunState.Uninitialized, RunState.Finished); + + try { + Thread.sleep(100); + } catch (Exception e) { + } + + assertThat(awaited).isFalse(); + + // Note that neither Stopped or Finished are required to be positive, + // as long as all others are zero total. + tally.remove(RunState.Running); + + try { + Thread.sleep(100); + } catch (Exception e) { + } + + assertThat(awaited).isTrue(); + assertThat(waiter.getState()).isNotEqualTo(Thread.State.RUNNABLE); + + } + + @Test + @Order(4) + public void testAwaitNoneOtherTimedOut() { + Thread.currentThread().setName("SETTER"); + + RunStateTally tally = new RunStateTally(); + tally.add(RunState.Uninitialized); + tally.add(RunState.Running); + Thread waiter = new Thread(new Runnable() { + @Override + public void run() { + event = tally.awaitNoneOther(1500, RunState.Stopped, RunState.Finished); + awaited = true; + } + }); + waiter.setName("WAITER"); + waiter.setDaemon(true); + waiter.start(); + + try { + Thread.sleep(100); + } catch (Exception e) { + } + + assertThat(awaited).isFalse(); + tally.change(RunState.Uninitialized, RunState.Finished); + + try { + Thread.sleep(1500); + } catch (Exception e) { + } + +// try { +// waiter.join(); +// } catch (InterruptedException e) { +// throw new RuntimeException(e); +// } + + assertThat(event.isOnly(RunState.Errored)).isFalse(); + assertThat(waiter.getState()).isNotEqualTo(Thread.State.RUNNABLE); + + } + + +} diff --git a/engine-core/src/test/java/io/nosqlbench/engine/core/CoreMotorTest.java b/engine-core/src/test/java/io/nosqlbench/engine/core/CoreMotorTest.java index 7f12dd609..bcc1d6ecc 100644 --- a/engine-core/src/test/java/io/nosqlbench/engine/core/CoreMotorTest.java +++ b/engine-core/src/test/java/io/nosqlbench/engine/core/CoreMotorTest.java @@ -38,7 +38,6 @@ public class CoreMotorTest { Motor cm = new CoreMotor(activity, 5L, lockstepper); AtomicLong observableAction = new AtomicLong(-3L); cm.setAction(getTestConsumer(observableAction)); - cm.getSlotStateTracker().enterState(RunState.Starting); Thread t = new Thread(cm); t.setName("TestMotor"); t.start(); @@ -58,7 +57,6 @@ public class CoreMotorTest { AtomicLongArray ary = new AtomicLongArray(10); Action a1 = getTestArrayConsumer(ary); cm1.setAction(a1); - cm1.getSlotStateTracker().enterState(RunState.Starting); Thread t1 = new Thread(cm1); t1.setName("cm1"); From b9365bff725c8746e69d56ef936fd5f791924fc8 Mon Sep 17 00:00:00 2001 From: Jonathan Shook Date: Tue, 20 Dec 2022 20:04:17 -0600 Subject: [PATCH 14/19] improve concurrency patterns for activity execution --- .../engine/api/activityapi/core/Motor.java | 5 +- .../api/activityimpl/motor/CoreMotor.java | 119 ++--- .../core/lifecycle/ActivityFinisher.java | 50 -- ...esult.java => ExecutionMetricsResult.java} | 6 +- .../{ExecResult.java => ExecutionResult.java} | 22 +- .../core/lifecycle/StartedActivityInfo.java | 29 -- .../activity/ActivitiesExceptionHandler.java | 40 ++ .../ActivityExecutor.java} | 459 +++++++++--------- .../activity/ActivityRuntimeInfo.java | 97 ++++ .../scenario}/Scenario.java | 68 +-- .../scenario/ScenarioController.java | 281 ++++------- .../lifecycle/scenario/ScenariosExecutor.java | 7 +- .../lifecycle/scenario/ScenariosResults.java | 6 +- ...gerTest.java => ActivityExecutorTest.java} | 84 +++- 14 files changed, 624 insertions(+), 649 deletions(-) delete mode 100644 engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ActivityFinisher.java rename engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/{ExecMetricsResult.java => ExecutionMetricsResult.java} (95%) rename engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/{ExecResult.java => ExecutionResult.java} (75%) delete mode 100644 engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/StartedActivityInfo.java create mode 100644 engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/activity/ActivitiesExceptionHandler.java rename engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/{ActivityThreadsManager.java => activity/ActivityExecutor.java} (62%) create mode 100644 engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/activity/ActivityRuntimeInfo.java rename engine-core/src/main/java/io/nosqlbench/engine/core/{script => lifecycle/scenario}/Scenario.java (88%) rename engine-core/src/test/java/io/nosqlbench/engine/core/{ActivityThreadsManagerTest.java => ActivityExecutorTest.java} (71%) diff --git a/engine-api/src/main/java/io/nosqlbench/engine/api/activityapi/core/Motor.java b/engine-api/src/main/java/io/nosqlbench/engine/api/activityapi/core/Motor.java index 9ad35f877..ee208e4fd 100644 --- a/engine-api/src/main/java/io/nosqlbench/engine/api/activityapi/core/Motor.java +++ b/engine-api/src/main/java/io/nosqlbench/engine/api/activityapi/core/Motor.java @@ -17,7 +17,7 @@ package io.nosqlbench.engine.api.activityapi.core; import io.nosqlbench.engine.api.activityapi.input.Input; -import io.nosqlbench.engine.api.activityimpl.SlotStateTracker; +import io.nosqlbench.engine.api.activityimpl.MotorState; /** * The core threading harness within an activity. @@ -54,6 +54,7 @@ public interface Motor extends Runnable, Stoppable { * Get a description of the current slot run status. * @return - a value from the {@link RunState} enum */ - SlotStateTracker getSlotStateTracker(); + MotorState getState(); + void removeState(); } diff --git a/engine-api/src/main/java/io/nosqlbench/engine/api/activityimpl/motor/CoreMotor.java b/engine-api/src/main/java/io/nosqlbench/engine/api/activityimpl/motor/CoreMotor.java index d31028ecc..cb074b910 100644 --- a/engine-api/src/main/java/io/nosqlbench/engine/api/activityimpl/motor/CoreMotor.java +++ b/engine-api/src/main/java/io/nosqlbench/engine/api/activityimpl/motor/CoreMotor.java @@ -29,12 +29,11 @@ import io.nosqlbench.engine.api.activityapi.input.Input; import io.nosqlbench.engine.api.activityapi.output.Output; import io.nosqlbench.engine.api.activityapi.ratelimits.RateLimiter; import io.nosqlbench.api.engine.activityimpl.ActivityDef; -import io.nosqlbench.engine.api.activityimpl.SlotStateTracker; +import io.nosqlbench.engine.api.activityimpl.MotorState; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; import static io.nosqlbench.engine.api.activityapi.core.RunState.*; @@ -70,8 +69,8 @@ public class CoreMotor implements ActivityDefObserver, Motor, Stoppable { private final Activity activity; private Output output; - private final SlotStateTracker slotStateTracker; - private final AtomicReference slotState; + private final MotorState motorState; + // private final AtomicReference slotState; private int stride = 1; private OpTracker opTracker; @@ -86,14 +85,13 @@ public class CoreMotor implements ActivityDefObserver, Motor, Stoppable { * @param input A LongSupplier which provides the cycle number inputs. */ public CoreMotor( - Activity activity, - long slotId, - Input input) { + Activity activity, + long slotId, + Input input) { this.activity = activity; this.slotId = slotId; setInput(input); - slotStateTracker = new SlotStateTracker(slotId); - slotState = slotStateTracker.getAtomicSlotState(); + motorState = new MotorState(slotId, activity.getRunStateTally()); onActivityDefUpdate(activity.getActivityDef()); } @@ -107,10 +105,10 @@ public class CoreMotor implements ActivityDefObserver, Motor, Stoppable { * @param action An LongConsumer which is applied to the input for each cycle. */ public CoreMotor( - Activity activity, - long slotId, - Input input, - Action action + Activity activity, + long slotId, + Input input, + Action action ) { this(activity, slotId, input); setAction(action); @@ -126,11 +124,11 @@ public class CoreMotor implements ActivityDefObserver, Motor, Stoppable { * @param output An optional opTracker. */ public CoreMotor( - Activity activity, - long slotId, - Input input, - Action action, - Output output + Activity activity, + long slotId, + Input input, + Action action, + Output output ) { this(activity, slotId, input); setAction(action); @@ -178,12 +176,18 @@ public class CoreMotor implements ActivityDefObserver, Motor, Stoppable { } @Override - public SlotStateTracker getSlotStateTracker() { - return slotStateTracker; + public MotorState getState() { + return motorState; + } + + @Override + public void removeState() { + motorState.removeState(); } @Override public void run() { + motorState.enterState(Starting); try { inputTimer = activity.getInstrumentation().getOrCreateInputTimer(); @@ -195,12 +199,10 @@ public class CoreMotor implements ActivityDefObserver, Motor, Stoppable { cycleRateLimiter = activity.getCycleLimiter(); - if (slotState.get() == Finished) { + if (motorState.get() == Finished) { logger.warn("Input was already exhausted for slot " + slotId + ", remaining in finished state."); } - slotStateTracker.enterState(Running); - long cyclenum; action.init(); @@ -235,7 +237,8 @@ public class CoreMotor implements ActivityDefObserver, Motor, Stoppable { strideconsumer = (StrideOutputConsumer) async; } - while (slotState.get() == Running) { + motorState.enterState(Running); + while (motorState.get() == Running) { CycleSegment cycleSegment = null; @@ -245,7 +248,7 @@ public class CoreMotor implements ActivityDefObserver, Motor, Stoppable { if (cycleSegment == null) { logger.trace("input exhausted (input " + input + ") via null segment, stopping motor thread " + slotId); - slotStateTracker.enterState(Finished); + motorState.enterState(Finished); continue; } @@ -256,27 +259,27 @@ public class CoreMotor implements ActivityDefObserver, Motor, Stoppable { StrideTracker strideTracker = new StrideTracker<>( strideServiceTimer, - stridesResponseTimer, - strideDelay, - cycleSegment.peekNextCycle(), - stride, - output, - strideconsumer); + stridesResponseTimer, + strideDelay, + cycleSegment.peekNextCycle(), + stride, + output, + strideconsumer); strideTracker.start(); long strideStart = System.nanoTime(); - while (!cycleSegment.isExhausted() && slotState.get() == Running) { + while (!cycleSegment.isExhausted() && motorState.get() == Running) { cyclenum = cycleSegment.nextCycle(); if (cyclenum < 0) { if (cycleSegment.isExhausted()) { logger.trace("input exhausted (input " + input + ") via negative read, stopping motor thread " + slotId); - slotStateTracker.enterState(Finished); + motorState.enterState(Finished); continue; } } - if (slotState.get() != Running) { + if (motorState.get() != Running) { logger.trace("motor stopped in cycle " + cyclenum + ", stopping motor thread " + slotId); continue; } @@ -287,7 +290,7 @@ public class CoreMotor implements ActivityDefObserver, Motor, Stoppable { } try { - TrackedOp op = opTracker.newOp(cyclenum,strideTracker); + TrackedOp op = opTracker.newOp(cyclenum, strideTracker); op.setWaitTime(cycleDelay); synchronized (opTracker) { @@ -312,7 +315,7 @@ public class CoreMotor implements ActivityDefObserver, Motor, Stoppable { } - if (slotState.get() == Finished) { + if (motorState.get() == Finished) { boolean finished = opTracker.awaitCompletion(60000); if (finished) { logger.debug("slot " + this.slotId + " completed successfully"); @@ -321,12 +324,12 @@ public class CoreMotor implements ActivityDefObserver, Motor, Stoppable { } } - if (slotState.get() == Stopping) { - slotStateTracker.enterState(Stopped); + if (motorState.get() == Stopping) { + motorState.enterState(Stopped); } - } else if (action instanceof SyncAction) { + } else if (action instanceof SyncAction sync) { cycleServiceTimer = activity.getInstrumentation().getOrCreateCyclesServiceTimer(); strideServiceTimer = activity.getInstrumentation().getOrCreateStridesServiceTimer(); @@ -335,9 +338,8 @@ public class CoreMotor implements ActivityDefObserver, Motor, Stoppable { throw new RuntimeException("The async parameter was given for this activity, but it does not seem to know how to do async."); } - SyncAction sync = (SyncAction) action; - - while (slotState.get() == Running) { + motorState.enterState(Running); + while (motorState.get() == Running) { CycleSegment cycleSegment = null; CycleResultSegmentBuffer segBuffer = new CycleResultSegmentBuffer(stride); @@ -348,7 +350,7 @@ public class CoreMotor implements ActivityDefObserver, Motor, Stoppable { if (cycleSegment == null) { logger.trace("input exhausted (input " + input + ") via null segment, stopping motor thread " + slotId); - slotStateTracker.enterState(Finished); + motorState.enterState(Finished); continue; } @@ -366,12 +368,12 @@ public class CoreMotor implements ActivityDefObserver, Motor, Stoppable { if (cyclenum < 0) { if (cycleSegment.isExhausted()) { logger.trace("input exhausted (input " + input + ") via negative read, stopping motor thread " + slotId); - slotStateTracker.enterState(Finished); + motorState.enterState(Finished); continue; } } - if (slotState.get() != Running) { + if (motorState.get() != Running) { logger.trace("motor stopped after input (input " + cyclenum + "), stopping motor thread " + slotId); continue; } @@ -391,6 +393,9 @@ public class CoreMotor implements ActivityDefObserver, Motor, Stoppable { result = sync.runCycle(cyclenum); long phaseEnd = System.nanoTime(); + } catch (Exception e) { + motorState.enterState(Errored); + throw e; } finally { long cycleEnd = System.nanoTime(); cycleServiceTimer.update((cycleEnd - cycleStart) + cycleDelay, TimeUnit.NANOSECONDS); @@ -414,25 +419,29 @@ public class CoreMotor implements ActivityDefObserver, Motor, Stoppable { } } - if (slotState.get() == Stopping) { - slotStateTracker.enterState(Stopped); - } - } else { throw new RuntimeException("Valid Action implementations must implement either the SyncAction or the AsyncAction sub-interface"); } + if (motorState.get() == Stopping) { + motorState.enterState(Stopped); + logger.trace(() -> Thread.currentThread().getName() + " shutting down as " + motorState.get()); + } else if (motorState.get() == Finished) { + logger.trace(() -> Thread.currentThread().getName() + " shutting down as " + motorState.get()); + } else { + logger.warn("Unexpected motor state for CoreMotor shutdown: " + motorState.get()); + } } catch (Throwable t) { logger.error("Error in core motor loop:" + t, t); + motorState.enterState(Errored); throw t; } } - @Override public String toString() { - return "slot:" + this.slotId + "; state:" + slotState.get(); + return "slot:" + this.slotId + "; state:" + motorState.get(); } @Override @@ -452,17 +461,17 @@ public class CoreMotor implements ActivityDefObserver, Motor, Stoppable { @Override public synchronized void requestStop() { - if (slotState.get() == Running) { + if (motorState.get() == Running) { if (input instanceof Stoppable) { ((Stoppable) input).requestStop(); } if (action instanceof Stoppable) { ((Stoppable) action).requestStop(); } - slotStateTracker.enterState(RunState.Stopping); + motorState.enterState(RunState.Stopping); } else { - if (slotState.get() != Stopped && slotState.get() != Stopping) { - logger.warn("attempted to stop motor " + this.getSlotId() + ": from non Running state:" + slotState.get()); + if (motorState.get() != Stopped && motorState.get() != Stopping) { + logger.warn("attempted to stop motor " + this.getSlotId() + ": from non Running state:" + motorState.get()); } } } diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ActivityFinisher.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ActivityFinisher.java deleted file mode 100644 index 5b8588d80..000000000 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ActivityFinisher.java +++ /dev/null @@ -1,50 +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; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -public class ActivityFinisher extends Thread { - private final static Logger logger = LogManager.getLogger(ActivityFinisher.class); - - private final ActivityThreadsManager executor; - private final int timeout; - private boolean result; - - public ActivityFinisher(ActivityThreadsManager executor, int timeout) { - super(executor.getActivityDef().getAlias() + "_finisher"); - this.executor = executor; - this.timeout = timeout; - } - - @Override - public void run() { - logger.debug(this + " awaiting async completion of " + executor.getActivity().getAlias() + " on " + executor + " for timeout " + timeout); - result = executor.awaitCompletion(timeout); - logger.debug(this + " awaited async completion of " + executor.getActivity().getAlias()); - } - - public boolean getResult() { - return result; - } - - @Override - public String toString() { - return this.getClass().getSimpleName()+"/" + executor.getActivity().getAlias(); - } -} diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ExecMetricsResult.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ExecutionMetricsResult.java similarity index 95% rename from engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ExecMetricsResult.java rename to engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ExecutionMetricsResult.java index f20b943b5..e81360e56 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ExecMetricsResult.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ExecutionMetricsResult.java @@ -28,7 +28,7 @@ import java.util.HashSet; import java.util.Set; import java.util.concurrent.TimeUnit; -public class ExecMetricsResult extends ExecResult { +public class ExecutionMetricsResult extends ExecutionResult { public static final Set INTERVAL_ONLY_METRICS = Set.of( MetricAttribute.MIN, @@ -48,8 +48,8 @@ public class ExecMetricsResult extends ExecResult { MetricAttribute.M15_RATE ); - public ExecMetricsResult(long startedAt, long endedAt, String iolog, Exception e) { - super(startedAt, endedAt, iolog, e); + public ExecutionMetricsResult(long startedAt, long endedAt, String iolog, Exception error) { + super(startedAt, endedAt, iolog, error); } public String getMetricsSummary() { diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ExecResult.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ExecutionResult.java similarity index 75% rename from engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ExecResult.java rename to engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ExecutionResult.java index db1454143..5db6c12d9 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ExecResult.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ExecutionResult.java @@ -19,28 +19,26 @@ package io.nosqlbench.engine.core.lifecycle; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import java.util.Optional; - /** * Provide a result type back to a caller, including the start and end times, * any exception that occurred, and any content written to stdout or stderr equivalent * IO streams. This is an execution result. * */ -public class ExecResult { - protected final static Logger logger = LogManager.getLogger(ExecMetricsResult.class); +public class ExecutionResult { + protected final static Logger logger = LogManager.getLogger(ExecutionMetricsResult.class); protected final long startedAt; protected final long endedAt; protected final Exception exception; protected final String iolog; - public ExecResult(long startedAt, long endedAt, String iolog, Exception e) { + public ExecutionResult(long startedAt, long endedAt, String iolog, Exception error) { this.startedAt = startedAt; this.endedAt = endedAt; - this.exception = e; - this.iolog = ((iolog != null) ? iolog + "\n\n" : "") + (e != null ? e.getMessage() : ""); - logger.debug("populating "+(e==null? "NORMAL" : "ERROR")+" scenario result"); - if (logger.isDebugEnabled()) { + this.exception = error; + this.iolog = ((iolog != null) ? iolog + "\n\n" : "") + exception; + logger.debug("populating "+(error==null ? "NORMAL" : "ERROR")+" scenario result"); + if (logger.isTraceEnabled()) { StackTraceElement[] st = Thread.currentThread().getStackTrace(); for (int i = 0; i < st.length; i++) { logger.debug(":AT " + st[i].getFileName()+":"+st[i].getLineNumber()+":"+st[i].getMethodName()); @@ -51,7 +49,7 @@ public class ExecResult { } public void reportElapsedMillisToLog() { - logger.info("-- SCENARIO TOOK " + getElapsedMillis() + "ms --"); + logger.info(() -> String.format("-- SCENARIO TOOK %.3fS --",(getElapsedMillis()/1000.0f))); } public String getIOLog() { @@ -62,7 +60,7 @@ public class ExecResult { return endedAt - startedAt; } - public Optional getException() { - return Optional.ofNullable(exception); + public Exception getException() { + return exception; } } diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/StartedActivityInfo.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/StartedActivityInfo.java deleted file mode 100644 index 28b7f76c9..000000000 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/StartedActivityInfo.java +++ /dev/null @@ -1,29 +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; - -import io.nosqlbench.engine.api.activityapi.core.Activity; - -public class StartedActivityInfo { - private final Activity activity; - - StartedActivityInfo(Activity activity) { - this.activity = activity; - } - - -} diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/activity/ActivitiesExceptionHandler.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/activity/ActivitiesExceptionHandler.java new file mode 100644 index 000000000..dd20c7948 --- /dev/null +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/activity/ActivitiesExceptionHandler.java @@ -0,0 +1,40 @@ +/* + * 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.activity; + +import io.nosqlbench.engine.core.lifecycle.scenario.ScenarioController; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +public class ActivitiesExceptionHandler implements Thread.UncaughtExceptionHandler { + + private static final Logger logger = LogManager.getLogger(ActivitiesExceptionHandler.class); + + private final ScenarioController controller; + + public ActivitiesExceptionHandler(ScenarioController controller) { + this.controller = controller; + logger.debug(() -> "Activity exception handler starting up for executor '" + this.controller + "'"); + } + + + @Override + public void uncaughtException(Thread t, Throwable e) { + logger.error("Uncaught exception in thread '" + t.getName() + ", state[" + t.getState() + "], notifying executor '" + controller + "'"); + controller.notifyException(t, e); + } +} diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ActivityThreadsManager.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/activity/ActivityExecutor.java similarity index 62% rename from engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ActivityThreadsManager.java rename to engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/activity/ActivityExecutor.java index afc53094e..e24aea1e0 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/ActivityThreadsManager.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/activity/ActivityExecutor.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.nosqlbench.engine.core.lifecycle; +package io.nosqlbench.engine.core.lifecycle.activity; import io.nosqlbench.api.annotations.Annotation; import io.nosqlbench.api.annotations.Layer; @@ -22,42 +22,48 @@ import io.nosqlbench.api.engine.activityimpl.ParameterMap; import io.nosqlbench.engine.api.activityapi.core.*; 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 org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.concurrent.*; import java.util.stream.Collectors; /** - *

An ActivityExecutor is a named instance of an execution harness for a single activity instance. + *

An ActivityExecutor is an execution harness for a single activity instance. * It is responsible for managing threads and activity settings which may be changed while the activity is running.

* - *

An ActivityExecutor may be represent an activity that is defined and active in the running - * scenario, but which is inactive. This can occur when an activity is paused by controlling logic, or when the threads - * are set to zero.

+ *

In order to allow for dynamic thread management, which is not easily supported as an explicit feature + * of most executor services, threads are started as long-running processes and managed via state signaling. + * The {@link RunState} enum, {@link io.nosqlbench.engine.api.activityimpl.MotorState} type, and {@link RunStateTally} + * state tracking class are used together to represent valid states and transitions, contain and transition state atomically, + * and provide blocking conditions for observers, respectively.

+ * + *

Some basic rules and invariants must be observed for consistent concurrent behavior. + * Any state changes for a Motor must be made through {@link Motor#getState()}. + * This allows the state tracking to work consistently for all observers.

* - *

- * Invariants: - *

- *
    - *
  • Motors may not receive parameter updates before their owning activities are initialized.
  • - *
*/ -public class ActivityThreadsManager implements ActivityController, ParameterMap.Listener, ProgressCapable, Callable { +public class ActivityExecutor implements ActivityController, ParameterMap.Listener, ProgressCapable, Callable { - private static final Logger logger = LogManager.getLogger(ActivityThreadsManager.class); + // TODO Encapsulate valid state transitions to be only modifiable within the appropriate type view. + + private static final Logger logger = LogManager.getLogger(ActivityExecutor.class); private static final Logger activitylogger = LogManager.getLogger("ACTIVITY"); private final List> motors = new ArrayList<>(); private final Activity activity; private final ActivityDef activityDef; - private final ExecutorService executorService; - private RuntimeException stoppingException; + private final RunStateTally tally; + private ExecutorService executorService; + private Exception exception; private final static int waitTime = 10000; private String sessionId = ""; @@ -67,75 +73,32 @@ public class ActivityThreadsManager implements ActivityController, ParameterMap. // private RunState intendedState = RunState.Uninitialized; - public ActivityThreadsManager(Activity activity, String sessionId) { + public ActivityExecutor(Activity activity, String sessionId) { this.activity = activity; this.activityDef = activity.getActivityDef(); - executorService = new ThreadPoolExecutor( - 0, Integer.MAX_VALUE, - 0L, TimeUnit.SECONDS, - new SynchronousQueue<>(), - new IndexedThreadFactory(activity.getAlias(), new ActivityExceptionHandler(this)) - ); activity.getActivityDef().getParams().addListener(this); activity.setActivityController(this); this.sessionId = sessionId; + this.tally = activity.getRunStateTally(); } // TODO: Doc how uninitialized activities do not propagate parameter map changes and how // TODO: this is different from preventing modification to uninitialized activities - /** - *

True-up the number of motor instances known to the executor. Start all non-running motors. - * The protocol between the motors and the executor should be safe as long as each state change is owned by either - * the motor logic or the activity executor but not both, and strictly serialized as well. This is enforced by - * forcing start(...) to be serialized as well as using CAS on the motor states.

- *

The startActivity method may be called to true-up the number of active motors in an activity executor after - * changes to threads.

- */ - public synchronized void startActivity() { - logger.info("starting activity " + activity.getAlias() + " for cycles " + activity.getCycleSummary()); - Annotators.recordAnnotation(Annotation.newBuilder() - .session(sessionId) - .now() - .layer(Layer.Activity) - .label("alias", getActivityDef().getAlias()) - .label("driver", getActivityDef().getActivityType()) - .label("workload", getActivityDef().getParams().getOptionalString("workload").orElse("none")) - .detail("params", getActivityDef().toString()) - .build() - ); - - activitylogger.debug("START/before alias=(" + activity.getAlias() + ")"); - try { - activity.setRunState(RunState.Starting); - this.startedAt = System.currentTimeMillis(); - activity.initActivity(); - //activity.onActivityDefUpdate(activityDef); - } catch (Exception e) { - this.stoppingException = new RuntimeException("Error initializing activity '" + activity.getAlias() + "':\n" + e.getMessage(), e); -// activitylogger.error("error initializing activity '" + activity.getAlias() + "': " + stoppingException); - throw stoppingException; - } - adjustToActivityDef(activity.getActivityDef()); - activity.setRunState(RunState.Running); - activitylogger.debug("START/after alias=(" + activity.getAlias() + ")"); - } - /** * Simply stop the motors */ - private synchronized void stopActivity() { - activitylogger.debug("STOP/before alias=(" + activity.getAlias() + ")"); - - activity.setRunState(RunState.Stopping); + public void stopActivity() { logger.info("stopping activity in progress: " + this.getActivityDef().getAlias()); + activity.setRunState(RunState.Stopping); motors.forEach(Motor::requestStop); - motors.forEach(m -> awaitRequiredMotorState(m, 30000, 50, RunState.Stopped, RunState.Finished)); - activity.shutdownActivity(); - activity.closeAutoCloseables(); + tally.awaitNoneOther(RunState.Stopped,RunState.Finished); + + shutdownExecutorService(Integer.MAX_VALUE); + tally.awaitNoneOther(RunState.Stopped,RunState.Finished); activity.setRunState(RunState.Stopped); + logger.info("stopped: " + this.getActivityDef().getAlias() + " with " + motors.size() + " slots"); - activitylogger.debug("STOP/after alias=(" + activity.getAlias() + ")"); Annotators.recordAnnotation(Annotation.newBuilder() .session(sessionId) @@ -149,7 +112,7 @@ public class ActivityThreadsManager implements ActivityController, ParameterMap. ); } - public RuntimeException forceStopActivity(int initialMillisToWait) { + public Exception forceStopActivity(int initialMillisToWait) { activitylogger.debug("FORCE STOP/before alias=(" + activity.getAlias() + ")"); activity.setRunState(RunState.Stopped); @@ -188,10 +151,10 @@ public class ActivityThreadsManager implements ActivityController, ParameterMap. logger.debug("took " + (activityShutdownEndedAt - activityShutdownStartedAt) + " ms to shutdown activity threads"); activitylogger.debug("FORCE STOP/after alias=(" + activity.getAlias() + ")"); - if (stoppingException != null) { + if (exception != null) { activitylogger.debug("FORCE STOP/exception alias=(" + activity.getAlias() + ")"); } - return stoppingException; + return exception; } @@ -201,60 +164,19 @@ public class ActivityThreadsManager implements ActivityController, ParameterMap. * @param initialMillisToWait milliseconds to wait after graceful shutdownActivity request, before forcing * everything to stop */ - private synchronized void forceStopScenarioAndThrow(int initialMillisToWait, boolean rethrow) { - RuntimeException exception = forceStopActivity(initialMillisToWait); + public synchronized void forceStopScenarioAndThrow(int initialMillisToWait, boolean rethrow) { + Exception exception = forceStopActivity(initialMillisToWait); if (exception != null && rethrow) { - throw exception; + throw new RuntimeException(exception); } } - private boolean finishAndShutdownExecutor(int secondsToWait) { - - activitylogger.debug("REQUEST STOP/before alias=(" + activity.getAlias() + ")"); - logger.debug("Stopping executor for " + activity.getAlias() + " when work completes."); - - boolean wasStopped = false; - try { - executorService.shutdown(); - logger.trace(() -> "awaiting termination with timeout of " + secondsToWait + " seconds"); - wasStopped = executorService.awaitTermination(secondsToWait, TimeUnit.SECONDS); - } catch (InterruptedException ie) { - logger.trace("interrupted while awaiting termination"); - wasStopped = false; - logger.warn("while waiting termination of shutdown " + activity.getAlias() + ", " + ie.getMessage()); - activitylogger.debug("REQUEST STOP/exception alias=(" + activity.getAlias() + ") wasstopped=" + wasStopped); - } catch (RuntimeException e) { - logger.trace("Received exception while awaiting termination: " + e.getMessage()); - wasStopped = true; - stoppingException = e; - } finally { - - logger.trace(() -> "finally shutting down activity " + this.getActivity().getAlias()); - activity.shutdownActivity(); - - logger.trace("closing auto-closeables"); - activity.closeAutoCloseables(); - activity.setRunState(RunState.Stopped); - this.stoppedAt = System.currentTimeMillis(); - } - - if (stoppingException != null) { - logger.trace(() -> "an exception caused the activity to stop:" + stoppingException.getMessage()); - logger.trace("Setting ERROR on activity executor: " + stoppingException.getMessage()); - throw stoppingException; - } - - activitylogger.debug("REQUEST STOP/after alias=(" + activity.getAlias() + ") wasstopped=" + wasStopped); - - return wasStopped; - } - /** * Listens for changes to parameter maps, maps them to the activity instance, and notifies all eligible listeners of * changes. */ @Override - public synchronized void handleParameterMapUpdate(ParameterMap parameterMap) { + public void handleParameterMapUpdate(ParameterMap parameterMap) { activity.onActivityDefUpdate(activityDef); @@ -263,7 +185,7 @@ public class ActivityThreadsManager implements ActivityController, ParameterMap. // by the RunState. if (activity.getRunState() != RunState.Uninitialized) { if (activity.getRunState() == RunState.Running) { - adjustToActivityDef(activity.getActivityDef()); + adjustMotorCountToThreadParam(activity.getActivityDef()); } motors.stream() .filter(m -> (m instanceof ActivityDefObserver)) @@ -286,7 +208,7 @@ public class ActivityThreadsManager implements ActivityController, ParameterMap. */ private boolean awaitCompletion(int waitTime) { logger.debug(() -> "awaiting completion of '" + this.getActivity().getAlias() + "'"); - boolean finished = finishAndShutdownExecutor(waitTime); + boolean finished = shutdownExecutorService(waitTime); Annotators.recordAnnotation(Annotation.newBuilder() .session(sessionId) @@ -302,28 +224,13 @@ public class ActivityThreadsManager implements ActivityController, ParameterMap. return finished; } - public boolean awaitFinishedOrStopped(int timeout) { - activitylogger.debug("AWAIT-FINISH/before alias=(" + activity.getAlias() + ")"); - - boolean awaited = awaitAllRequiredMotorState(timeout, 50, RunState.Finished, RunState.Stopped); - if (awaited) { - awaited = awaitCompletion(timeout); - } - if (stoppingException != null) { - activitylogger.debug("AWAIT-FINISH/exception alias=(" + activity.getAlias() + ")"); - throw stoppingException; - } - activitylogger.debug("AWAIT-FINISH/after alias=(" + activity.getAlias() + ")"); - return awaited; - } - public String toString() { return getClass().getSimpleName() + "~" + activityDef.getAlias(); } private String getSlotStatus() { return motors.stream() - .map(m -> m.getSlotStateTracker().getSlotState().getCode()) + .map(m -> m.getState().get().getCode()) .collect(Collectors.joining(",", "[", "]")); } @@ -332,17 +239,19 @@ public class ActivityThreadsManager implements ActivityController, ParameterMap. * * @param activityDef the activityDef for this activity instance */ - private synchronized void adjustToActivityDef(ActivityDef activityDef) { + private void adjustMotorCountToThreadParam(ActivityDef activityDef) { logger.trace(() -> ">-pre-adjust->" + getSlotStatus()); - // Stop and remove extra motor slots - while (motors.size() > activityDef.getThreads()) { - Motor motor = motors.get(motors.size() - 1); - logger.trace(() -> "Stopping cycle motor thread:" + motor); - motor.requestStop(); - motors.remove(motors.size() - 1); - } + reduceActiveMotorCountDownToThreadParam(activityDef); + increaseActiveMotorCountUpToThreadParam(activityDef); + alignMotorStateToIntendedActivityState(); + awaitAlignmentOfMotorStateToActivityState(); + logger.trace(() -> ">post-adjust->" + getSlotStatus()); + + } + + private void increaseActiveMotorCountUpToThreadParam(ActivityDef activityDef) { // Create motor slots while (motors.size() < activityDef.getThreads()) { @@ -350,15 +259,27 @@ public class ActivityThreadsManager implements ActivityController, ParameterMap. logger.trace(() -> "Starting cycle motor thread:" + motor); motors.add(motor); } - - applyIntendedStateToDivergentMotors(); - awaitActivityAndMotorStateAlignment(); - - logger.trace(() -> ">post-adjust->" + getSlotStatus()); - } - private void applyIntendedStateToDivergentMotors() { + private void reduceActiveMotorCountDownToThreadParam(ActivityDef activityDef) { + // Stop and remove extra motor slots + while (motors.size() > activityDef.getThreads()) { + Motor motor = motors.get(motors.size() - 1); + logger.trace(() -> "Stopping cycle motor thread:" + motor); + motor.requestStop(); + motor.removeState(); + + /** + * NOTE: this leaves trailing, longer-running threads which might hold the executor open + * to potentially be cleaned up by {@link ExecutorService#shutdown()} or + * {@link ExecutorService#shutdownNow()}. At this point, the motor thread has + * been instructed to shutdown, and it is effectively thread-non-grata to the activity. + */ + motors.remove(motors.size() - 1); + } + } + + private void alignMotorStateToIntendedActivityState() { RunState intended = activity.getRunState(); logger.trace(() -> "ADJUSTING to INTENDED " + intended); switch (intended) { @@ -367,17 +288,16 @@ public class ActivityThreadsManager implements ActivityController, ParameterMap. case Running: case Starting: motors.stream() - .filter(m -> m.getSlotStateTracker().getSlotState() != RunState.Running) - .filter(m -> m.getSlotStateTracker().getSlotState() != RunState.Finished) - .filter(m -> m.getSlotStateTracker().getSlotState() != RunState.Starting) + .filter(m -> m.getState().get() != RunState.Running) + .filter(m -> m.getState().get() != RunState.Finished) + .filter(m -> m.getState().get() != RunState.Starting) .forEach(m -> { - m.getSlotStateTracker().enterState(RunState.Starting); executorService.execute(m); }); break; case Stopped: motors.stream() - .filter(m -> m.getSlotStateTracker().getSlotState() != RunState.Stopped) + .filter(m -> m.getState().get() != RunState.Stopped) .forEach(Motor::requestStop); break; case Finished: @@ -389,20 +309,21 @@ public class ActivityThreadsManager implements ActivityController, ParameterMap. } } - private void awaitActivityAndMotorStateAlignment() { + private void awaitAlignmentOfMotorStateToActivityState() { + logger.debug(()->"awaiting state alignment from " + activity.getRunState()); switch (activity.getRunState()) { case Starting: case Running: - motors.forEach(m -> awaitRequiredMotorState(m, waitTime, 50, RunState.Running, RunState.Finished)); + tally.awaitNoneOther(RunState.Running, RunState.Finished); break; case Stopped: - motors.forEach(m -> awaitRequiredMotorState(m, waitTime, 50, RunState.Stopped, RunState.Finished)); + tally.awaitNoneOther(RunState.Stopped, RunState.Finished); break; case Uninitialized: break; case Finished: - motors.forEach(m -> awaitRequiredMotorState(m, waitTime, 50, RunState.Finished)); + tally.awaitNoneOther(RunState.Finished); break; case Stopping: throw new RuntimeException("Invalid requested state in activity executor:" + activity.getRunState()); @@ -413,106 +334,40 @@ public class ActivityThreadsManager implements ActivityController, ParameterMap. } - /** - * Await a thread (aka motor/slot) entering a specific SlotState - * - * @param m motor instance - * @param waitTime milliseconds to wait, total - * @param pollTime polling interval between state checks - * @param desiredRunStates any desired SlotState - * @return true, if the desired SlotState was detected - */ - private boolean awaitMotorState(Motor m, int waitTime, int pollTime, RunState... desiredRunStates) { - long startedAt = System.currentTimeMillis(); - while (System.currentTimeMillis() < (startedAt + waitTime)) { - for (RunState desiredRunState : desiredRunStates) { - if (desiredRunState == m.getSlotStateTracker().getSlotState()) { - return true; - } - } - try { - Thread.sleep(pollTime); - } catch (InterruptedException ignored) { - } - } - logger.trace(() -> activityDef.getAlias() + "/Motor[" + m.getSlotId() + "] is now in state " + m.getSlotStateTracker().getSlotState()); - return false; - } - - private boolean awaitAllRequiredMotorState(int waitTime, int pollTime, RunState... awaitingState) { - long startedAt = System.currentTimeMillis(); - boolean awaited = false; - while (!awaited && (System.currentTimeMillis() < (startedAt + waitTime))) { - awaited = true; - for (Motor motor : motors) { - awaited = awaitMotorState(motor, waitTime, pollTime, awaitingState); - if (!awaited) { - logger.trace(() -> "failed awaiting motor " + motor.getSlotId() + " for state in " + - Arrays.asList(awaitingState)); - break; - } - } - } - return awaited; - } - - /** - * Await a required thread (aka motor/slot) entering a specific SlotState - * - * @param m motor instance - * @param waitTime milliseconds to wait, total - * @param pollTime polling interval between state checks - * @param awaitingState desired SlotState - * @throws RuntimeException if the waitTime is used up and the desired state is not reached - */ - private void awaitRequiredMotorState(Motor m, int waitTime, int pollTime, RunState... awaitingState) { - RunState startingState = m.getSlotStateTracker().getSlotState(); - boolean awaitedRequiredState = awaitMotorState(m, waitTime, pollTime, awaitingState); - if (!awaitedRequiredState) { - String error = "Unable to await " + activityDef.getAlias() + - "/Motor[" + m.getSlotId() + "]: from state " + startingState + " to " + m.getSlotStateTracker().getSlotState() - + " after waiting for " + waitTime + "ms"; - RuntimeException e = new RuntimeException(error); - logger.error(error); - throw e; - } - logger.trace(() -> "motor " + m + " entered awaited state: " + Arrays.asList(awaitingState)); - } - - private synchronized void requestStopMotors() { + private void requestStopMotors() { logger.info("stopping activity " + activity); - activity.setRunState(RunState.Stopped); + activity.setRunState(RunState.Stopping); motors.forEach(Motor::requestStop); } public boolean isRunning() { - return motors.stream().anyMatch(m -> m.getSlotStateTracker().getSlotState() == RunState.Running); + return motors.stream().anyMatch(m -> m.getState().get() == RunState.Running); } public Activity getActivity() { return activity; } - public synchronized void notifyException(Thread t, Throwable e) { + public void notifyException(Thread t, Throwable e) { logger.debug(() -> "Uncaught exception in activity thread forwarded to activity executor: " + e.getMessage()); - this.stoppingException = new RuntimeException("Error in activity thread " + t.getName(), e); - forceStopActivity(10000); + this.exception = new RuntimeException("Error in activity thread " + t.getName(), e); + this.requestStopMotors(); } @Override public synchronized void stopActivityWithReasonAsync(String reason) { logger.info("Stopping activity " + this.activityDef.getAlias() + ": " + reason); - this.stoppingException = new RuntimeException("Stopping activity " + this.activityDef.getAlias() + ": " + reason); - logger.error("stopping with reason: " + stoppingException); + this.exception = new RuntimeException("Stopping activity " + this.activityDef.getAlias() + ": " + reason); + logger.error("stopping with reason: " + exception); requestStopMotors(); } @Override public synchronized void stopActivityWithErrorAsync(Throwable throwable) { - if (stoppingException == null) { - this.stoppingException = new RuntimeException(throwable); + if (exception == null) { + this.exception = new RuntimeException(throwable); logger.error("stopping on error: " + throwable.toString(), throwable); } else { if (activityDef.getParams().getOptionalBoolean("fullerrors").orElse(false)) { @@ -531,9 +386,139 @@ public class ActivityThreadsManager implements ActivityController, ParameterMap. @Override - public synchronized ExecResult call() throws Exception { - boolean stopped = awaitCompletion(Integer.MAX_VALUE); - ExecResult result = new ExecResult(startedAt, stoppedAt, "", this.stoppingException); + public ExecutionResult call() throws Exception { + + try { + // instantiate and configure fixtures that need to be present + // before threads start running such as metrics instruments + activity.initActivity(); + awaitMotorsAtLeastRunning(); + awaitActivityCompletion(); + activity.shutdownActivity(); + activity.closeAutoCloseables(); + } catch (Exception e) { + this.exception = e; + } + ExecutionResult result = new ExecutionResult(startedAt, stoppedAt, "", exception); return result; } + + /** + * This waits for at least one motor to be in running, finished or stopped state. + * A motor with enough cycles to read will go into a running state. A motor which has + * a short read immediately after being started will go into a finished state. A motor + * which has been stopped for some reason, like an error or a stop command will go into + * stopped state. All of these states are sufficient to signal that successful startup + * has been completed at least. + */ + private void awaitMotorsAtLeastRunning() { + RunStateImage states = tally.awaitAny(RunState.Running, RunState.Stopped, RunState.Finished, RunState.Errored); + RunState maxState = states.getMaxState(); + if (maxState==RunState.Errored) { + activity.setRunState(maxState); + throw new RuntimeException("Error in activity"); + } + } + + public void startActivity() { + // we need an executor service to run motor threads on + startMotorExecutorService(); + startRunningActivityThreads(); + awaitMotorsAtLeastRunning(); + } + + private boolean shutdownExecutorService(int secondsToWait) { + + activitylogger.debug(() -> "Shutting down motor executor for (" + activity.getAlias() + ")"); + + boolean wasStopped = false; + try { + executorService.shutdown(); + logger.trace(() -> "awaiting termination with timeout of " + secondsToWait + " seconds"); + wasStopped = executorService.awaitTermination(secondsToWait, TimeUnit.SECONDS); + } catch (InterruptedException ie) { + logger.trace("interrupted while awaiting termination"); + wasStopped = false; + logger.warn("while waiting termination of shutdown " + activity.getAlias() + ", " + ie.getMessage()); + activitylogger.debug("REQUEST STOP/exception alias=(" + activity.getAlias() + ") wasstopped=" + wasStopped); + } catch (RuntimeException e) { + logger.trace("Received exception while awaiting termination: " + e.getMessage()); + wasStopped = true; + exception = e; + } finally { + logger.trace(() -> "finally shutting down activity " + this.getActivity().getAlias()); + this.stoppedAt = System.currentTimeMillis(); + activity.setRunState(RunState.Stopped); + } + + if (exception != null) { + logger.trace(() -> "an exception caused the activity to stop:" + exception.getMessage()); + logger.warn("Setting ERROR on motor executor for activity '" + activity.getAlias() + "': " + exception.getMessage()); + throw new RuntimeException(exception); + } + + activitylogger.debug("motor executor for " + activity.getAlias() + ") wasstopped=" + wasStopped); + + return wasStopped; + } + + private void awaitActivityCompletion() { + RunStateImage state = tally.awaitNoneOther(RunState.Stopped, RunState.Finished, RunState.Errored); + RunState maxState = state.getMaxState(); + activity.setRunState(maxState); + if (maxState==RunState.Errored) { + throw new RuntimeException("Error while waiting for activity completion:" + this.exception); + } + } + + private void startMotorExecutorService() { + this.executorService = new ThreadPoolExecutor( + 0, Integer.MAX_VALUE, + 0L, TimeUnit.SECONDS, + new SynchronousQueue<>(), + new IndexedThreadFactory(activity.getAlias(), new ActivityExceptionHandler(this)) + ); + } + + + /** + *

True-up the number of motor instances known to the executor. Start all non-running motors. + * The protocol between the motors and the executor should be safe as long as each state change is owned by either + * the motor logic or the activity executor but not both, and strictly serialized as well. This is enforced by + * forcing start(...) to be serialized as well as using CAS on the motor states.

+ *

The startActivity method may be called to true-up the number of active motors in an activity executor after + * changes to threads.

+ */ + private void startRunningActivityThreads() { + + logger.info("starting activity " + activity.getAlias() + " for cycles " + activity.getCycleSummary()); + Annotators.recordAnnotation(Annotation.newBuilder() + .session(sessionId) + .now() + .layer(Layer.Activity) + .label("alias", getActivityDef().getAlias()) + .label("driver", getActivityDef().getActivityType()) + .label("workload", getActivityDef().getParams().getOptionalString("workload").orElse("none")) + .detail("params", getActivityDef().toString()) + .build() + ); + + activitylogger.debug("START/before alias=(" + activity.getAlias() + ")"); + + try { + activity.setRunState(RunState.Starting); + this.startedAt = System.currentTimeMillis(); + activity.onActivityDefUpdate(activityDef); + } catch (Exception e) { + this.exception = new RuntimeException("Error initializing activity '" + activity.getAlias() + "':\n" + e.getMessage(), e); + activitylogger.error(()->"error initializing activity '" + activity.getAlias() + "': " + exception); + throw new RuntimeException(exception); + } + adjustMotorCountToThreadParam(activity.getActivityDef()); + tally.awaitAny(RunState.Running,RunState.Finished,RunState.Stopped); + activity.setRunState(RunState.Running); + activitylogger.debug("START/after alias=(" + activity.getAlias() + ")"); + } + + } diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/activity/ActivityRuntimeInfo.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/activity/ActivityRuntimeInfo.java new file mode 100644 index 000000000..8e3d0ec53 --- /dev/null +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/activity/ActivityRuntimeInfo.java @@ -0,0 +1,97 @@ +/* + * 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.activity; + +import io.nosqlbench.engine.api.activityapi.core.Activity; +import io.nosqlbench.engine.api.activityapi.core.RunState; +import io.nosqlbench.engine.api.activityapi.core.progress.ProgressCapable; +import io.nosqlbench.engine.api.activityapi.core.progress.ProgressMeterDisplay; +import io.nosqlbench.engine.core.lifecycle.ExecutionResult; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +public class ActivityRuntimeInfo implements ProgressCapable { + + private final static Logger logger = LogManager.getLogger(ActivityRuntimeInfo.class); + + private final Activity activity; + private final Future future; + private final ActivityExecutor executor; + + private ExecutionResult result; + + public ActivityRuntimeInfo(Activity activity, Future result, ActivityExecutor executor) { + + this.activity = activity; + this.future = result; + this.executor = executor; + } + + @Override + public ProgressMeterDisplay getProgressMeter() { + return this.activity.getProgressMeter(); + } + + + public Future getFuture() { + return this.future; + } + + /** + * Wait until the execution is complete and return the result. + * @param timeoutMillis + * @return null, or an ExecutionResult if the execution completed + */ + public ExecutionResult awaitResult(long timeoutMillis) { + ExecutionResult result = null; + try { + result = future.get(timeoutMillis, TimeUnit.MILLISECONDS); + } catch (TimeoutException te) { + } catch (InterruptedException ie) { + logger.warn("interrupted waiting for execution to complete"); + } catch (ExecutionException e) { + throw new RuntimeException(e); +// return new ExecutionResult(activity.getStartedAtMillis(),System.currentTimeMillis(),"",e); + } + return result; + } + + public Activity getActivity() { + return this.activity; + } + + public boolean isRunning() { + return executor.isRunning(); + } + + public RunState getRunState() { + return this.activity.getRunState(); + } + + public void stopActivity() { + this.executor.stopActivity(); + } + + public ActivityExecutor getActivityExecutor() { + return executor; + } +} diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/script/Scenario.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/Scenario.java similarity index 88% rename from engine-core/src/main/java/io/nosqlbench/engine/core/script/Scenario.java rename to engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/Scenario.java index a087b444d..42f1a3545 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/script/Scenario.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/Scenario.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package io.nosqlbench.engine.core.script; +package io.nosqlbench.engine.core.lifecycle.scenario; import com.codahale.metrics.MetricRegistry; import com.oracle.truffle.js.scriptengine.GraalJSScriptEngine; @@ -26,11 +26,14 @@ import io.nosqlbench.api.metadata.SystemId; import io.nosqlbench.engine.api.extensions.ScriptingPluginInfo; import io.nosqlbench.engine.api.scripting.ScriptEnvBuffer; import io.nosqlbench.engine.core.annotation.Annotators; -import io.nosqlbench.engine.core.lifecycle.ActivityProgressIndicator; -import io.nosqlbench.engine.core.lifecycle.ExecMetricsResult; -import io.nosqlbench.engine.core.lifecycle.PolyglotScenarioController; -import io.nosqlbench.engine.core.lifecycle.ScenarioController; -import io.nosqlbench.engine.core.metrics.PolyglotMetricRegistryBindings; +import io.nosqlbench.engine.core.lifecycle.activity.ActivityProgressIndicator; +import io.nosqlbench.engine.core.lifecycle.ExecutionMetricsResult; +import io.nosqlbench.engine.core.lifecycle.scenario.script.bindings.PolyglotScenarioController; +import io.nosqlbench.engine.core.lifecycle.scenario.script.bindings.ActivityBindings; +import io.nosqlbench.engine.core.lifecycle.scenario.script.SandboxExtensionFinder; +import io.nosqlbench.engine.core.lifecycle.scenario.script.ScenarioContext; +import io.nosqlbench.engine.core.lifecycle.scenario.script.ScriptParams; +import io.nosqlbench.engine.core.lifecycle.scenario.script.bindings.PolyglotMetricRegistryBindings; import io.nosqlbench.nb.annotations.Maturity; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -58,7 +61,7 @@ import java.util.Optional; import java.util.concurrent.Callable; import java.util.stream.Collectors; -public class Scenario implements Callable { +public class Scenario implements Callable { private final String commandLine; private final String reportSummaryTo; @@ -71,9 +74,9 @@ public class Scenario implements Callable { private Exception error; private ScenarioMetadata scenarioMetadata; - private ExecMetricsResult result; + private ExecutionMetricsResult result; - public Optional getResultIfComplete() { + public Optional getResultIfComplete() { return Optional.ofNullable(this.result); } @@ -213,7 +216,7 @@ public class Scenario implements Callable { scriptEngine.put("scenario", new PolyglotScenarioController(scenarioController)); scriptEngine.put("metrics", new PolyglotMetricRegistryBindings(metricRegistry)); - scriptEngine.put("activities", new NashornActivityBindings(scenarioController)); + scriptEngine.put("activities", new ActivityBindings(scenarioController)); for (ScriptingPluginInfo extensionDescriptor : SandboxExtensionFinder.findAll()) { if (!extensionDescriptor.isAutoLoading()) { @@ -263,20 +266,26 @@ public class Scenario implements Callable { ); logger.debug("Running control script for " + getScenarioName() + "."); - scenarioController = new ScenarioController(this.scenarioName, minMaturity); - initializeScriptingEngine(scenarioController); - executeScenarioScripts(); - - long awaitCompletionTime = 86400 * 365 * 1000L; - logger.debug("Awaiting completion of scenario and activities for " + awaitCompletionTime + " millis."); - - scenarioController.awaitCompletion(awaitCompletionTime); - //TODO: Ensure control flow covers controller shutdown in event of internal error. + scenarioController = new ScenarioController(this,minMaturity); + try { + initializeScriptingEngine(scenarioController); + executeScenarioScripts(); + long awaitCompletionTime = 86400 * 365 * 1000L; + logger.debug("Awaiting completion of scenario and activities for " + awaitCompletionTime + " millis."); + scenarioController.awaitCompletion(awaitCompletionTime); + } catch (Exception e) { + this.error=e; + } finally { + scenarioController.shutdown(); + } Runtime.getRuntime().removeShutdownHook(scenarioShutdownHook); scenarioShutdownHook.run(); } + public void notifyException(Thread t, Throwable e) { + this.error=new RuntimeException("in thread " + t.getName() + ", " +e, e); + } private void executeScenarioScripts() { for (String script : scripts) { try { @@ -375,35 +384,32 @@ public class Scenario implements Callable { * * The lifecycle of a scenario includes the lifecycles of all of the following: *
    - *
  1. The scenario control script, executing within a graaljs context.
  2. - *
  3. The lifecycle of every activity which is started within the scenario.
  4. + *
  5. The scenario control script, executing within a graaljs context.
  6. + *
  7. The lifecycle of every activity which is started within the scenario.
  8. *
* * 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: *
    - *
  • The scenario control script has run to completion, or experienced an exception.
  • - *
  • Each activity has run to completion, experienced an exception, or all
  • + *
  • The scenario control script has run to completion, or experienced an exception.
  • + *
  • Each activity has run to completion, experienced an exception, or all
  • *
* * @return */ - public synchronized ExecMetricsResult call() { + public synchronized ExecutionMetricsResult call() { if (result == null) { try { runScenario(); } catch (Exception e) { - if (this.error!=null) { - logger.debug("OVERLAPPING ERRORS: prior" + this.error.getMessage() + ", current:" + e.getMessage()); - } - this.error = e; + this.error=e; } finally { - logger.debug((this.error == null ? "NORMAL" : "ERRORED") + " scenario run"); + logger.debug((this.error==null ? "NORMAL" : "ERRORED") + " scenario run"); } String iolog = scriptEnv.getTimedLog(); - this.result = new ExecMetricsResult(this.error, iolog, this.startedAtMillis, this.endedAtMillis); + this.result = new ExecutionMetricsResult(this.startedAtMillis, this.endedAtMillis, iolog, error); result.reportMetricsSummaryToLog(); doReportSummaries(reportSummaryTo, result); } @@ -411,7 +417,7 @@ public class Scenario implements Callable { return result; } - private void doReportSummaries(String reportSummaryTo, ExecMetricsResult result) { + private void doReportSummaries(String reportSummaryTo, ExecutionMetricsResult result) { List fullChannels = new ArrayList<>(); List briefChannels = new ArrayList<>(); diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/ScenarioController.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/ScenarioController.java index bef6a8c2e..d0b2c4c4f 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/ScenarioController.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/ScenarioController.java @@ -70,7 +70,7 @@ public class ScenarioController { */ public synchronized void start(ActivityDef activityDef) { Annotators.recordAnnotation(Annotation.newBuilder() - .session(sessionId) + .session(scenario.getScenarioName()) .now() .layer(Layer.Activity) .label("alias", activityDef.getAlias()) @@ -129,7 +129,7 @@ public class ScenarioController { */ public synchronized void run(ActivityDef activityDef, long timeoutMs) { Annotators.recordAnnotation(Annotation.newBuilder() - .session(sessionId) + .session(this.scenario.getScenarioName()) .now() .layer(Layer.Activity) .label("alias", activityDef.getAlias()) @@ -165,9 +165,8 @@ public class ScenarioController { } public boolean isRunningActivity(ActivityDef activityDef) { - - ActivityThreadsManager activityThreadsManager = getActivityExecutor(activityDef, false); - return activityThreadsManager != null && activityThreadsManager.isRunning(); + ActivityRuntimeInfo runtimeInfo = this.activityInfoMap.get(activityDef.getAlias()); + return (runtimeInfo != null && runtimeInfo.isRunning()); } public boolean isRunningActivity(Map activityDefMap) { @@ -184,7 +183,7 @@ public class ScenarioController { */ public synchronized void stop(ActivityDef activityDef) { Annotators.recordAnnotation(Annotation.newBuilder() - .session(sessionId) + .session(this.scenario.getScenarioName()) .now() .layer(Layer.Activity) .label("alias", activityDef.getAlias()) @@ -192,18 +191,14 @@ public class ScenarioController { .detail("params", activityDef.toString()) .build()); - ActivityThreadsManager activityThreadsManager = getActivityExecutor(activityDef, false); - if (activityThreadsManager == null) { + ActivityRuntimeInfo runtimeInfo = this.activityInfoMap.get(activityDef.getAlias()); + if (runtimeInfo == null) { throw new RuntimeException("could not stop missing activity:" + activityDef); } - RunState runstate = activityThreadsManager.getActivity().getRunState(); - if (runstate != RunState.Running) { - logger.warn("NOT stopping activity '" + activityThreadsManager.getActivity().getAlias() + "' because it is in state '" + runstate + "'"); - return; - } scenariologger.debug("STOP " + activityDef.getAlias()); - activityThreadsManager.stopActivity(); + + runtimeInfo.stopActivity(); } /** @@ -240,76 +235,6 @@ public class ScenarioController { } } - /** - * Modify one of the parameters in a defined activity. Any observing activity components will be notified of the - * changes made to activity parameters. - * - * @param alias The name of an activity that is already known to the scenario. - * @param param The parameter name - * @param value a new parameter value - */ - public synchronized void modify(String alias, String param, String value) { - if (param.equals("alias")) { - throw new InvalidParameterException("It is not allowed to change the name of an existing activity."); - } - ActivityThreadsManager activityThreadsManager = getActivityExecutor(alias); - ParameterMap params = activityThreadsManager.getActivityDef().getParams(); - scenariologger.debug("SET (" + alias + "/" + param + ")=(" + value + ")"); - params.set(param, value); - } - - /** - * Apply any parameter changes to a defined activity, or start a new one. - * This method is syntactical sugar for scripting. Each of the parameters in the map - * is checked against existing values, and per-field modifications - * are applied one at a time, only if the values have changed. - * - * @param appliedParams Map of new values. - */ - public synchronized void apply(Map appliedParams) { - String alias = appliedParams.get("alias"); - - if (alias == null) { - throw new BasicError("alias must be provided"); - } - - ActivityThreadsManager executor = activityExecutors.get(alias); - - if (executor == null) { - logger.info("started scenario from apply:" + alias); - start(appliedParams); - return; - } - - ParameterMap previousMap = executor.getActivityDef().getParams(); - - for (String paramName : appliedParams.keySet()) { - String appliedVal = appliedParams.get(paramName); - Optional prevVal = previousMap.getOptionalString(paramName); - - if (!prevVal.isPresent() || !prevVal.get().equals(appliedVal)) { - logger.info("applying new value to activity '" + alias + "': '" + prevVal.get() + "' -> '" + appliedVal + "'"); - previousMap.set(paramName, appliedVal); - } - } - } - - /** - * Get the activity executor associated with the given alias. This should be used to find activitytypes - * which are presumed to be already defined. - * - * @param activityAlias The activity alias for the extant activity. - * @return the associated ActivityExecutor - * @throws RuntimeException a runtime exception if the named activity is not found - */ - private ActivityThreadsManager getActivityExecutor(String activityAlias) { - Optional executor = - Optional.ofNullable(activityExecutors.get(activityAlias)); - return executor.orElseThrow( - () -> new RuntimeException("ActivityExecutor for alias " + activityAlias + " not found.") - ); - - } private List getMatchingAliases(String pattern) { Pattern matcher; @@ -320,52 +245,13 @@ public class ScenarioController { matcher = Pattern.compile(pattern); } - List matching = activityExecutors.keySet().stream() + List matching = activityInfoMap.keySet().stream() .filter(a -> Pattern.matches(pattern, a)) .peek(p -> logger.debug("MATCH " + pattern + " -> " + p)) .collect(Collectors.toList()); return matching; } - private ActivityThreadsManager getActivityExecutor(ActivityDef activityDef, boolean createIfMissing) { - synchronized (activityExecutors) { - ActivityThreadsManager executor = activityExecutors.get(activityDef.getAlias()); - - if (executor == null && createIfMissing) { - if (activityDef.getParams().containsKey("driver")) { - ActivityType activityType = new ActivityTypeLoader() - .setMaturity(this.minMaturity) - .load(activityDef) - .orElseThrow( - () -> new RuntimeException("Driver for '" + activityDef + "' was not found." + - "\nYou can use --list-drivers to see what drivers are supported in this runtime." + - ConfigSuggestions.suggestAlternates( - new ActivityTypeLoader().getAllSelectors(), activityDef.getActivityType(), 4) - .orElse("") - ) - ); - - executor = new ActivityThreadsManager( - activityType.getAssembledActivity( - activityDef, - getActivityMap() - ), - this.sessionId - ); - activityExecutors.put(activityDef.getAlias(), executor); - } else { - executor = new ActivityThreadsManager( - new StandardActivityType(activityDef).getAssembledActivity( - activityDef, getActivityMap() - ), this.sessionId - ); - } - - } - return executor; - } - } - /** * Wait for a bit. This is not the best approach, and will be replaced with a different system in the future. * @@ -394,29 +280,7 @@ public class ScenarioController { * @return set of activity names */ public Set getAliases() { - return activityExecutors.keySet(); - } - - /** - * Return all the activity definitions that are known to this scenario. - * - * @return list of activity defs - */ - public List getActivityDefs() { - return activityExecutors.values().stream() - .map(ActivityThreadsManager::getActivityDef) - .collect(Collectors.toList()); - } - - /** - * Get the named activity def, if it is known to this scenario. - * - * @param alias The name by which the activity is known to this scenario. - * @return an ActivityDef instance - * @throws RuntimeException if the alias is not known to the scenario - */ - public ActivityDef getActivityDef(String alias) { - return getActivityExecutor(alias).getActivityDef(); + return activityInfoMap.keySet(); } /** @@ -427,8 +291,9 @@ public class ScenarioController { * @param waitTimeMillis grace period during which an activity may cooperatively shut down */ public synchronized void forceStopScenario(int waitTimeMillis, boolean rethrow) { + logger.debug("force stopping scenario " + this.scenario.getScenarioName()); + activityInfoMap.values().forEach(a -> a.getActivityExecutor().forceStopActivity(10000)); logger.debug("Scenario force stopped."); - activityExecutors.values().forEach(a -> a.forceStopScenarioAndThrow(waitTimeMillis, rethrow)); } // public synchronized void stopAll() { @@ -446,31 +311,21 @@ public class ScenarioController { public boolean awaitCompletion(long waitTimeMillis) { logger.debug(() -> "awaiting completion"); boolean completed = true; - long remaining = waitTimeMillis; - - List finishers = new ArrayList<>(); - for (ActivityThreadsManager ae : activityExecutors.values()) { - ActivityFinisher finisher = new ActivityFinisher(ae, (int) remaining); - finishers.add(finisher); - finisher.start(); - } - - for (ActivityFinisher finisher : finishers) { - try { - logger.debug("joining finisher " + finisher.getName()); - finisher.join(waitTimeMillis); - logger.debug("joined finisher " + finisher.getName()); - } catch (InterruptedException ignored) { - } - } - - for (ActivityFinisher finisher : finishers) { - if (!finisher.getResult()) { - logger.debug("finisher for " + finisher.getName() + " did not signal TRUE"); + for (ActivityRuntimeInfo activityRuntimeInfo : this.activityInfoMap.values()) { + ExecutionResult activityResult = activityRuntimeInfo.awaitResult(waitTimeMillis); + if (activityResult == null) { + logger.error("Unable to retrieve activity result for " + activityRuntimeInfo.getActivity().getAlias()); completed = false; + } else { + if (activityResult.getException()!=null) { + if (activityResult.getException() instanceof RuntimeException e) { + throw e; + } else { + throw new RuntimeException(activityResult.getException()); + } + } } } - return completed; } @@ -482,66 +337,98 @@ public class ScenarioController { } } - public boolean await(Map activityDefMap) { - return this.awaitActivity(activityDefMap); + public void await(Map activityDefMap) { + this.awaitActivity(activityDefMap); } public boolean awaitActivity(Map activityDefMap) { ActivityDef ad = new ActivityDef(new ParameterMap(activityDefMap)); - return awaitActivity(ad); + return awaitActivity(ad, Long.MAX_VALUE); } public boolean await(String alias) { - return this.awaitActivity(alias); + return this.awaitActivity(alias, Long.MAX_VALUE); } - public boolean awaitActivity(String alias) { + public boolean awaitActivity(String alias, long timeoutMs) { ActivityDef toAwait = aliasToDef(alias); - return awaitActivity(toAwait); + return awaitActivity(toAwait, Long.MAX_VALUE); } - public boolean await(ActivityDef activityDef) { - return this.awaitActivity(activityDef); + public void await(ActivityDef activityDef, long timeoutMs) { + this.awaitActivity(activityDef, timeoutMs); } - public boolean awaitActivity(ActivityDef activityDef) { - ActivityThreadsManager activityThreadsManager = getActivityExecutor(activityDef, false); - if (activityThreadsManager == null) { - throw new RuntimeException("Could not await missing activity: " + activityDef); + public boolean awaitActivity(ActivityDef activityDef, long timeoutMs) { + ActivityRuntimeInfo ari = this.activityInfoMap.get(activityDef.getAlias()); + if (ari == null) { + throw new RuntimeException("Could not await missing activity: " + activityDef.getAlias()); } scenariologger.debug("AWAIT/before alias=" + activityDef.getAlias()); - boolean finished = activityThreadsManager.awaitFinishedOrStopped(Integer.MAX_VALUE); - scenariologger.debug("AWAIT/after completed=" + finished); - return finished; - + ExecutionResult result = null; + Future future=null; + try { + future = ari.getFuture(); + } catch (Exception e) { + throw new RuntimeException(e); + } + try { + result = future.get(timeoutMs, TimeUnit.MILLISECONDS); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } catch (TimeoutException e) { + throw new RuntimeException(e); + } + return (result != null); } /** * @return an unmodifyable String to executor map of all activities known to this scenario */ - public Map getActivityExecutorMap() { - return Collections.unmodifiableMap(activityExecutors); + public Map getActivityExecutorMap() { + return Collections.unmodifiableMap(activityInfoMap); + } + + public List getActivityDefs() { + return activityInfoMap.values().stream().map(ari -> ari.getActivity().getActivityDef()).toList(); } public void reportMetrics() { ActivityMetrics.reportTo(System.out); } - private Map getActivityMap() { - Map activityMap = new HashMap(); - for (Map.Entry entry : activityExecutors.entrySet()) { - activityMap.put(entry.getKey(), entry.getValue().getActivity()); - } - return activityMap; - } - public List getProgressMeters() { List indicators = new ArrayList<>(); - for (ActivityThreadsManager ae : activityExecutors.values()) { + for (ActivityRuntimeInfo ae : activityInfoMap.values()) { indicators.add(ae.getProgressMeter()); } indicators.sort(Comparator.comparing(ProgressMeterDisplay::getStartTime)); return indicators; } + public void notifyException(Thread t, Throwable e) { + logger.error("Uncaught exception in activity lifecycle thread:" + e, e); + scenario.notifyException(t,e); + throw new RuntimeException(e); + } + + public ActivityDef getActivityDef(String alias) { + return activityInfoMap.get(alias).getActivity().getActivityDef(); + } + + public void shutdown() { + this.activitiesExecutor.shutdown(); + try { + if (!this.activitiesExecutor.awaitTermination(5, TimeUnit.SECONDS)) { + this.activitiesExecutor.shutdownNow(); + if (!this.activitiesExecutor.awaitTermination(5, TimeUnit.SECONDS)) { + throw new RuntimeException("Unable to shutdown activities executor"); + } + } + } catch (Exception e) { + + } + } } diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/ScenariosExecutor.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/ScenariosExecutor.java index b644b84d1..3217a0910 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/ScenariosExecutor.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/ScenariosExecutor.java @@ -79,6 +79,7 @@ public class ScenariosExecutor { * @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"); } @@ -98,6 +99,7 @@ public class ScenariosExecutor { } catch (InterruptedException ignored) { } } + logger.trace("waited " + (System.currentTimeMillis()-waitFrom) + " millis for scenarios"); updateAt = Math.min(timeoutAt, System.currentTimeMillis() + updateInterval); } @@ -193,10 +195,7 @@ public class ScenariosExecutor { logger.debug("#stopScenario(name=" + scenarioName + ", rethrow="+ rethrow+")"); Optional pendingScenario = getPendingScenario(scenarioName); if (pendingScenario.isPresent()) { - ScenarioController controller = pendingScenario.get().getScenarioController(); - if (controller != null) { - controller.forceStopScenario(0, rethrow); - } + pendingScenario.get().getScenarioController().forceStopScenario(10000, true); } else { throw new RuntimeException("Unable to cancel scenario: " + scenarioName + ": not found"); } diff --git a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/ScenariosResults.java b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/ScenariosResults.java index 2cd5e763b..6e601f8c8 100644 --- a/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/ScenariosResults.java +++ b/engine-core/src/main/java/io/nosqlbench/engine/core/lifecycle/scenario/ScenariosResults.java @@ -41,8 +41,8 @@ public class ScenariosResults { public String getExecutionSummary() { String sb = "executions: " + scenarioResultMap.size() + " scenarios, " + - scenarioResultMap.values().stream().filter(r -> r.getException().isEmpty()).count() + " normal, " + - scenarioResultMap.values().stream().filter(r -> r.getException().isPresent()).count() + " errored"; + scenarioResultMap.values().stream().filter(r -> r.getException()==null).count() + " normal, " + + scenarioResultMap.values().stream().filter(r -> r.getException()!=null).count() + " errored"; return sb; } @@ -72,7 +72,7 @@ public class ScenariosResults { public boolean hasError() { return this.scenarioResultMap.values().stream() - .anyMatch(r -> r.getException().isPresent()); + .anyMatch(r -> r.getException()!=null); } public int getSize() { diff --git a/engine-core/src/test/java/io/nosqlbench/engine/core/ActivityThreadsManagerTest.java b/engine-core/src/test/java/io/nosqlbench/engine/core/ActivityExecutorTest.java similarity index 71% rename from engine-core/src/test/java/io/nosqlbench/engine/core/ActivityThreadsManagerTest.java rename to engine-core/src/test/java/io/nosqlbench/engine/core/ActivityExecutorTest.java index b82c1cd29..86f7d6165 100644 --- a/engine-core/src/test/java/io/nosqlbench/engine/core/ActivityThreadsManagerTest.java +++ b/engine-core/src/test/java/io/nosqlbench/engine/core/ActivityExecutorTest.java @@ -16,30 +16,35 @@ package io.nosqlbench.engine.core; +import io.nosqlbench.api.engine.activityimpl.ActivityDef; import io.nosqlbench.engine.api.activityapi.core.*; -import io.nosqlbench.engine.api.activityapi.output.OutputDispenser; import io.nosqlbench.engine.api.activityapi.input.Input; import io.nosqlbench.engine.api.activityapi.input.InputDispenser; -import io.nosqlbench.api.engine.activityimpl.ActivityDef; +import io.nosqlbench.engine.api.activityapi.output.OutputDispenser; import io.nosqlbench.engine.api.activityimpl.CoreServices; import io.nosqlbench.engine.api.activityimpl.SimpleActivity; import io.nosqlbench.engine.api.activityimpl.action.CoreActionDispenser; -import io.nosqlbench.engine.api.activityimpl.input.CoreInputDispenser; import io.nosqlbench.engine.api.activityimpl.input.AtomicInput; +import io.nosqlbench.engine.api.activityimpl.input.CoreInputDispenser; import io.nosqlbench.engine.api.activityimpl.motor.CoreMotor; import io.nosqlbench.engine.api.activityimpl.motor.CoreMotorDispenser; -import io.nosqlbench.engine.core.lifecycle.ActivityThreadsManager; -import io.nosqlbench.engine.core.lifecycle.ActivityTypeLoader; -import org.apache.logging.log4j.Logger; +import io.nosqlbench.engine.core.lifecycle.ExecutionResult; +import io.nosqlbench.engine.core.lifecycle.activity.ActivityExecutor; +import io.nosqlbench.engine.core.lifecycle.activity.ActivityTypeLoader; import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.junit.jupiter.api.Test; import java.util.Optional; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; import static org.assertj.core.api.Assertions.assertThat; -public class ActivityThreadsManagerTest { - private static final Logger logger = LogManager.getLogger(ActivityThreadsManagerTest.class); +public class ActivityExecutorTest { + private static final Logger logger = LogManager.getLogger(ActivityExecutorTest.class); @Test public synchronized void testRestart() { @@ -54,13 +59,30 @@ public class ActivityThreadsManagerTest { a.setOutputDispenserDelegate(tdisp); a.setInputDispenserDelegate(idisp); a.setMotorDispenserDelegate(mdisp); - - ActivityThreadsManager ae = new ActivityThreadsManager(a, "test-restart"); - ad.setThreads(1); - ae.startActivity(); - ae.stopActivity(); - ae.startActivity(); - ae.awaitCompletion(15000); + ExecutorService executor = Executors.newCachedThreadPool(); + ActivityExecutor ae = new ActivityExecutor(a, "test-restart"); + Future future = executor.submit(ae); + try { + System.out.println("ad.setThreads(1)"); + ad.setThreads(1); + System.out.println("ae.startActivity()"); + ae.startActivity(); + System.out.println("ae.stopActivity()"); + ae.stopActivity(); + System.out.println("ae.startActivity()"); + ae.startActivity(); + System.out.println("ae.startActivity()"); + ae.startActivity(); + System.out.println("ExecutionResult executionResult = future.get();"); + ExecutionResult executionResult = future.get(); + System.out.println("System.out.print(executionResult);"); + System.out.print(executionResult); + Thread.sleep(500L); + } catch (Exception e) { + throw new RuntimeException(e); + } + System.out.print("ad.setThreads(1)"); + executor.shutdown(); assertThat(idisp.getInput(10).getInputSegment(3)).isNull(); } @@ -79,10 +101,20 @@ public class ActivityThreadsManagerTest { a.setInputDispenserDelegate(idisp); a.setMotorDispenserDelegate(mdisp); - ActivityThreadsManager ae = new ActivityThreadsManager(a, "test-delayed-start"); - ad.setThreads(1); - ae.startActivity(); - ae.awaitCompletion(15000); + ActivityExecutor ae = new ActivityExecutor(a, "test-delayed-start"); + ExecutorService testExecutor = Executors.newCachedThreadPool(); + Future future = testExecutor.submit(ae); + + try { + ad.setThreads(1); + ae.startActivity(); + ExecutionResult result = future.get(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } catch (ExecutionException e) { + throw new RuntimeException(e); + } + testExecutor.shutdownNow(); assertThat(idisp.getInput(10).getInputSegment(3)).isNull(); } @@ -93,7 +125,7 @@ public class ActivityThreadsManagerTest { Optional activityType = new ActivityTypeLoader().load(ad); Input longSupplier = new AtomicInput(ad); MotorDispenser cmf = getActivityMotorFactory( - ad, motorActionDelay(999), longSupplier + ad, motorActionDelay(999), longSupplier ); Activity a = new SimpleActivity(ad); InputDispenser idisp = new CoreInputDispenser(a); @@ -104,15 +136,15 @@ public class ActivityThreadsManagerTest { a.setInputDispenserDelegate(idisp); a.setMotorDispenserDelegate(mdisp); - ActivityThreadsManager ae = new ActivityThreadsManager(a, "test-new-executor"); + ActivityExecutor ae = new ActivityExecutor(a, "test-new-executor"); ad.setThreads(5); ae.startActivity(); - int[] speeds = new int[]{1,2000,5,2000,2,2000}; - for(int offset=0; offset Date: Tue, 20 Dec 2022 23:11:26 -0600 Subject: [PATCH 15/19] test cleanup --- .../io/nosqlbench/engine/core/ActivityExecutorTest.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/engine-core/src/test/java/io/nosqlbench/engine/core/ActivityExecutorTest.java b/engine-core/src/test/java/io/nosqlbench/engine/core/ActivityExecutorTest.java index 86f7d6165..635c71a61 100644 --- a/engine-core/src/test/java/io/nosqlbench/engine/core/ActivityExecutorTest.java +++ b/engine-core/src/test/java/io/nosqlbench/engine/core/ActivityExecutorTest.java @@ -63,20 +63,12 @@ public class ActivityExecutorTest { ActivityExecutor ae = new ActivityExecutor(a, "test-restart"); Future future = executor.submit(ae); try { - System.out.println("ad.setThreads(1)"); ad.setThreads(1); - System.out.println("ae.startActivity()"); ae.startActivity(); - System.out.println("ae.stopActivity()"); ae.stopActivity(); - System.out.println("ae.startActivity()"); ae.startActivity(); - System.out.println("ae.startActivity()"); ae.startActivity(); - System.out.println("ExecutionResult executionResult = future.get();"); ExecutionResult executionResult = future.get(); - System.out.println("System.out.print(executionResult);"); - System.out.print(executionResult); Thread.sleep(500L); } catch (Exception e) { throw new RuntimeException(e); From 4f4b4982c867eff63e0fe8b9d0ed08c40a862436 Mon Sep 17 00:00:00 2001 From: Jonathan Shook Date: Tue, 20 Dec 2022 23:11:55 -0600 Subject: [PATCH 16/19] templatize test logging level for logger setup and root logger --- engine-api/src/test/resources/log4j2-test.xml | 4 +- engine-cli/src/test/resources/log4j2-test.xml | 4 +- .../src/test/resources/log4j2-test.xml | 4 +- .../src/test/resources/log4j2-test.xml | 4 +- mvn-defaults/pom.xml | 13 ++++- .../src/test/resources/log4j2-test.xml | 57 +++++++++++++++++++ nbr/src/test/resources/log4j2-test.xml | 57 +++++++++++++++++++ .../src/test/resources/log4j2-test.xml | 4 +- .../src/test/resources/log4j2-test.xml | 4 +- .../src/test/resources/log4j2-test.xml | 4 +- 10 files changed, 140 insertions(+), 15 deletions(-) create mode 100644 nbr-examples/src/test/resources/log4j2-test.xml create mode 100644 nbr/src/test/resources/log4j2-test.xml diff --git a/engine-api/src/test/resources/log4j2-test.xml b/engine-api/src/test/resources/log4j2-test.xml index 797a5780b..d3fa79da2 100644 --- a/engine-api/src/test/resources/log4j2-test.xml +++ b/engine-api/src/test/resources/log4j2-test.xml @@ -15,7 +15,7 @@ ~ limitations under the License. --> - @@ -48,7 +48,7 @@ - + diff --git a/engine-cli/src/test/resources/log4j2-test.xml b/engine-cli/src/test/resources/log4j2-test.xml index 797a5780b..d3fa79da2 100644 --- a/engine-cli/src/test/resources/log4j2-test.xml +++ b/engine-cli/src/test/resources/log4j2-test.xml @@ -15,7 +15,7 @@ ~ limitations under the License. --> - @@ -48,7 +48,7 @@ - + diff --git a/engine-core/src/test/resources/log4j2-test.xml b/engine-core/src/test/resources/log4j2-test.xml index 797a5780b..d3fa79da2 100644 --- a/engine-core/src/test/resources/log4j2-test.xml +++ b/engine-core/src/test/resources/log4j2-test.xml @@ -15,7 +15,7 @@ ~ limitations under the License. --> - @@ -48,7 +48,7 @@ - + diff --git a/engine-rest/src/test/resources/log4j2-test.xml b/engine-rest/src/test/resources/log4j2-test.xml index 3ee115d07..dc9a74051 100644 --- a/engine-rest/src/test/resources/log4j2-test.xml +++ b/engine-rest/src/test/resources/log4j2-test.xml @@ -15,7 +15,7 @@ ~ limitations under the License. --> - @@ -48,7 +48,7 @@ - + diff --git a/mvn-defaults/pom.xml b/mvn-defaults/pom.xml index 21b415e13..d74745189 100644 --- a/mvn-defaults/pom.xml +++ b/mvn-defaults/pom.xml @@ -23,6 +23,11 @@ pom + + INFO + + INFO + UTF-8 UTF-8 nosqlbench @@ -463,6 +468,12 @@ + + + src/test/resources + true + + org.apache.maven.plugins @@ -596,7 +607,7 @@ org.apache.rat apache-rat-plugin - 0.13 + 0.15 verify diff --git a/nbr-examples/src/test/resources/log4j2-test.xml b/nbr-examples/src/test/resources/log4j2-test.xml new file mode 100644 index 000000000..d3fa79da2 --- /dev/null +++ b/nbr-examples/src/test/resources/log4j2-test.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + %d %p %C{1.} [%t] %m%n + + + + + + + + + + + + + + + + + diff --git a/nbr/src/test/resources/log4j2-test.xml b/nbr/src/test/resources/log4j2-test.xml new file mode 100644 index 000000000..d3fa79da2 --- /dev/null +++ b/nbr/src/test/resources/log4j2-test.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + %d %p %C{1.} [%t] %m%n + + + + + + + + + + + + + + + + + diff --git a/virtdata-lang/src/test/resources/log4j2-test.xml b/virtdata-lang/src/test/resources/log4j2-test.xml index 797a5780b..d3fa79da2 100644 --- a/virtdata-lang/src/test/resources/log4j2-test.xml +++ b/virtdata-lang/src/test/resources/log4j2-test.xml @@ -15,7 +15,7 @@ ~ limitations under the License. --> - @@ -48,7 +48,7 @@ - + diff --git a/virtdata-lib-basics/src/test/resources/log4j2-test.xml b/virtdata-lib-basics/src/test/resources/log4j2-test.xml index 797a5780b..d3fa79da2 100644 --- a/virtdata-lib-basics/src/test/resources/log4j2-test.xml +++ b/virtdata-lib-basics/src/test/resources/log4j2-test.xml @@ -15,7 +15,7 @@ ~ limitations under the License. --> - @@ -48,7 +48,7 @@ - + diff --git a/virtdata-userlibs/src/test/resources/log4j2-test.xml b/virtdata-userlibs/src/test/resources/log4j2-test.xml index 797a5780b..d3fa79da2 100644 --- a/virtdata-userlibs/src/test/resources/log4j2-test.xml +++ b/virtdata-userlibs/src/test/resources/log4j2-test.xml @@ -15,7 +15,7 @@ ~ limitations under the License. --> - @@ -48,7 +48,7 @@ - + From 6c8782842da5c6ca78a060422a00e8595ed1a8cf Mon Sep 17 00:00:00 2001 From: Jonathan Shook Date: Tue, 20 Dec 2022 23:52:37 -0600 Subject: [PATCH 17/19] change tests System.out calls to use logger directly --- .../uniform/flowtypes/ChainingOp.java | 2 +- .../tracking/LongTreeTrackerTest2.java | 23 +++++++++++-------- .../api/templating/CommandTemplateTest.java | 5 +++- .../java/io/nosqlbench/nb/AggregateTests.java | 22 ++++++++++-------- .../optimizers/TestOptimoExperiments.java | 5 +++- .../rest/services/openapi/OpenApiLoader.java | 5 +++- .../services/openapi/OpenApiLoaderTest.java | 6 +++-- .../from_long/to_long/TriangularStepTest.java | 6 ++--- .../tests/long_long/SignedHashTest.java | 8 +++++-- .../tests/long_string/CombinationsTest.java | 5 +++- .../RealDistributionsConcurrencyTests.java | 17 +++++++------- .../RealDistributionsValuesTest.java | 13 +++++++---- .../IntegerDistributionsConcurrencyTest.java | 14 +++++------ .../IntegerDistributionsValuesTest.java | 13 +++++++---- .../virtdata/IntegratedComposerLogicTest.java | 3 +++ .../io/virtdata/IntegratedCurvesTest.java | 7 +++--- 16 files changed, 94 insertions(+), 60 deletions(-) diff --git a/adapters-api/src/main/java/io/nosqlbench/engine/api/activityimpl/uniform/flowtypes/ChainingOp.java b/adapters-api/src/main/java/io/nosqlbench/engine/api/activityimpl/uniform/flowtypes/ChainingOp.java index 43c1aeed4..019001331 100644 --- a/adapters-api/src/main/java/io/nosqlbench/engine/api/activityimpl/uniform/flowtypes/ChainingOp.java +++ b/adapters-api/src/main/java/io/nosqlbench/engine/api/activityimpl/uniform/flowtypes/ChainingOp.java @@ -19,7 +19,7 @@ package io.nosqlbench.engine.api.activityimpl.uniform.flowtypes; import java.util.function.Function; /** - *

ChainingOp: f(I) -> O

+ *

ChainingOp<I,O>: f(I) -> O<I,O>

*

* Run a function on the current cached result in the current thread and replace it * with the result of the function. ChainingOps are one way of invoking diff --git a/engine-api/src/test/java/io/nosqlbench/engine/api/activityimpl/tracking/LongTreeTrackerTest2.java b/engine-api/src/test/java/io/nosqlbench/engine/api/activityimpl/tracking/LongTreeTrackerTest2.java index a8406f6a7..6dbc889d0 100644 --- a/engine-api/src/test/java/io/nosqlbench/engine/api/activityimpl/tracking/LongTreeTrackerTest2.java +++ b/engine-api/src/test/java/io/nosqlbench/engine/api/activityimpl/tracking/LongTreeTrackerTest2.java @@ -18,12 +18,15 @@ package io.nosqlbench.engine.api.activityimpl.tracking; import io.nosqlbench.engine.api.activityimpl.marker.longheap.LongTreeTracker; import io.nosqlbench.engine.api.activityimpl.marker.longheap.LongTreeTrackerAtomic; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; public class LongTreeTrackerTest2 { + private final static Logger logger = LogManager.getLogger(LongTreeTrackerTest2.class); // @Test // public void testCoMask() { @@ -96,21 +99,21 @@ public class LongTreeTrackerTest2 { public void testApply() { LongTreeTracker t = new LongTreeTracker(0L); t.setCompleted(0); - System.out.println(t); + logger.debug(t); t.setCompleted(1); - System.out.println(t); + logger.debug(t); t.setCompleted(2); - System.out.println(t); + logger.debug(t); t.setCompleted(5); - System.out.println(t); + logger.debug(t); t.setCompleted(6); - System.out.println(t); + logger.debug(t); t.setCompleted(3); - System.out.println(t); + logger.debug(t); t.setCompleted(4); - System.out.println(t); + logger.debug(t); t.setCompleted(7); - System.out.println(t); + logger.debug(t); } @Test @@ -119,7 +122,7 @@ public class LongTreeTrackerTest2 { for (int i = 0; i < 32 ; i++) { t.setCompleted(i); } - System.out.println(t); + logger.debug(t); assertThat(t.getImage()).isEqualTo(-2L); } @@ -141,7 +144,7 @@ public class LongTreeTrackerTest2 { @Test public void testBitString() { LongTreeTracker t = new LongTreeTracker(2L); - System.out.println(t); + logger.debug(t); } /** diff --git a/engine-api/src/test/java/io/nosqlbench/engine/api/templating/CommandTemplateTest.java b/engine-api/src/test/java/io/nosqlbench/engine/api/templating/CommandTemplateTest.java index 48a4a021d..1041452b9 100644 --- a/engine-api/src/test/java/io/nosqlbench/engine/api/templating/CommandTemplateTest.java +++ b/engine-api/src/test/java/io/nosqlbench/engine/api/templating/CommandTemplateTest.java @@ -21,6 +21,8 @@ import com.google.gson.GsonBuilder; import io.nosqlbench.engine.api.activityconfig.StatementsLoader; import io.nosqlbench.engine.api.activityconfig.yaml.OpTemplate; import io.nosqlbench.engine.api.activityconfig.yaml.StmtsDocList; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.junit.jupiter.api.Test; import java.util.Map; @@ -28,6 +30,7 @@ import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; public class CommandTemplateTest { + private final static Logger logger = LogManager.getLogger(CommandTemplateTest.class); @Test public void testCommandTemplate() { @@ -53,7 +56,7 @@ public class CommandTemplateTest { OpTemplate optpl = stmtsDocs.getStmts().get(0); CommandTemplate ct = new CommandTemplate(optpl); String format = gson.toJson(ct); - System.out.println(format); + logger.debug(format); } diff --git a/engine-api/src/test/java/io/nosqlbench/nb/AggregateTests.java b/engine-api/src/test/java/io/nosqlbench/nb/AggregateTests.java index 9d8df75b1..62956734f 100644 --- a/engine-api/src/test/java/io/nosqlbench/nb/AggregateTests.java +++ b/engine-api/src/test/java/io/nosqlbench/nb/AggregateTests.java @@ -18,6 +18,8 @@ package io.nosqlbench.nb; import org.HdrHistogram.DoubleHistogram; import org.HdrHistogram.DoubleRecorder; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.junit.jupiter.api.Test; import java.util.DoubleSummaryStatistics; @@ -31,6 +33,7 @@ import static org.assertj.core.api.Assertions.assertThat; * a proof, so we can get on with testing! */ public class AggregateTests { + private final static Logger logger = LogManager.getLogger(AggregateTests.class); double[][] data = new double[][]{ {1, 1, 1, 1, 1, 1, 1, 1, 1, 91}, {15, 15, 15, 15, 15, 5, 5, 5, 5, 5}, @@ -62,8 +65,8 @@ public class AggregateTests { aggstats.accept(series.getAverage()); } - System.out.println("aggstats avg:" + aggstats.getAverage()); - System.out.println("allstats avg:" + allstats.getAverage()); + logger.debug("aggstats avg:" + aggstats.getAverage()); + logger.debug("allstats avg:" + allstats.getAverage()); assertThat(aggstats.getAverage()).isNotEqualTo(allstats.getAverage()); } @@ -92,21 +95,21 @@ public class AggregateTests { all.recordValue(v); } snapshots[i]=recorder.getIntervalHistogram(); - System.out.println(snapshot(snapshots[i],"ary[" + i + "]")); + logger.debug(snapshot(snapshots[i],"ary[" + i + "]")); } DoubleHistogram histoall = all.getIntervalHistogram(); - System.out.println(snapshot(histoall, "all")); + logger.debug(snapshot(histoall, "all")); for (double pctile : new double[]{10, 20, 30, 40, 50, 60, 70, 80, 90, 95, 99, 99.9, 99.99}) { DoubleSummaryStatistics avgOfInputs = new DoubleSummaryStatistics(); for (DoubleHistogram snapshot : snapshots) { avgOfInputs.accept(snapshot.getValueAtPercentile(pctile)); } - System.out.println("avg of " + pctile + " => " + String.format("%.3f",avgOfInputs.getAverage()) + " (min,max)=("+String.format("%.3f",avgOfInputs.getMin()) + "," + + logger.debug("avg of " + pctile + " => " + String.format("%.3f",avgOfInputs.getAverage()) + " (min,max)=("+String.format("%.3f",avgOfInputs.getMin()) + "," + String.format("%.3f",avgOfInputs.getMax())+ ")"); - System.out.println("direct " + pctile + " => " + String.format("%.3f",histoall.getValueAtPercentile(pctile))); - System.out.println(); + logger.debug("direct " + pctile + " => " + String.format("%.3f",histoall.getValueAtPercentile(pctile))); + } @@ -128,11 +131,10 @@ public class AggregateTests { prototype[j]=r.nextDouble()*100; } - System.out.print("proto[" + i + "] = "); + logger.debug("proto[" + i + "] = "); for (double v : prototype) { - System.out.print(String.format("% 3.0f ",v)); + logger.trace(String.format("% 3.0f ",v)); } - System.out.println(); series[i]=resampleCurve(prototype,100); } diff --git a/engine-extensions/src/test/java/io/nosqlbench/optimizers/TestOptimoExperiments.java b/engine-extensions/src/test/java/io/nosqlbench/optimizers/TestOptimoExperiments.java index 6a21cbd0e..9535297af 100644 --- a/engine-extensions/src/test/java/io/nosqlbench/optimizers/TestOptimoExperiments.java +++ b/engine-extensions/src/test/java/io/nosqlbench/optimizers/TestOptimoExperiments.java @@ -21,6 +21,8 @@ import org.apache.commons.math3.optim.*; import org.apache.commons.math3.optim.nonlinear.scalar.GoalType; import org.apache.commons.math3.optim.nonlinear.scalar.ObjectiveFunction; import org.apache.commons.math3.optim.nonlinear.scalar.noderiv.BOBYQAOptimizer; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.junit.jupiter.api.Test; import java.util.Arrays; @@ -52,6 +54,7 @@ import java.util.Random; */ public class TestOptimoExperiments { + private final static Logger logger = LogManager.getLogger(TestOptimoExperiments.class); @Test public void testNewAlgo() { @@ -81,7 +84,7 @@ public class TestOptimoExperiments { ); PointValuePair result = mo.optimize(od.toArray(new OptimizationData[0])); - System.out.println( + logger.debug( "point:" + Arrays.toString(result.getPoint()) + " value=" + m.value(result.getPoint()) ); diff --git a/engine-rest/src/main/java/io/nosqlbench/engine/rest/services/openapi/OpenApiLoader.java b/engine-rest/src/main/java/io/nosqlbench/engine/rest/services/openapi/OpenApiLoader.java index 2485d3e1f..1bc403b9f 100644 --- a/engine-rest/src/main/java/io/nosqlbench/engine/rest/services/openapi/OpenApiLoader.java +++ b/engine-rest/src/main/java/io/nosqlbench/engine/rest/services/openapi/OpenApiLoader.java @@ -27,6 +27,8 @@ import io.swagger.v3.oas.models.PathItem; import io.swagger.v3.oas.models.Paths; import io.swagger.v3.parser.core.models.ParseOptions; import io.swagger.v3.parser.core.models.SwaggerParseResult; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.introspector.BeanAccess; @@ -39,6 +41,7 @@ import java.util.stream.Collectors; * .md#securityRequirementObject">OpenApi Spec 3.1.0 */ public class OpenApiLoader { + private final static Logger logger = LogManager.getLogger(OpenApiLoader.class); private static final OpenAPIParser parser = new OpenAPIParser(); private static final Gson gson = new GsonBuilder().setPrettyPrinting().create(); @@ -91,7 +94,7 @@ public class OpenApiLoader { yaml.setBeanAccess(BeanAccess.DEFAULT); for (PathOp activeOp : activeOps) { - System.out.println("yaml for op:" + yaml.dump(activeOp)); + logger.debug("yaml for op:" + yaml.dump(activeOp)); pathops.put(activeOp.getCall(), activeOp); } diff --git a/engine-rest/src/test/java/io/nosqlbench/engine/rest/services/openapi/OpenApiLoaderTest.java b/engine-rest/src/test/java/io/nosqlbench/engine/rest/services/openapi/OpenApiLoaderTest.java index 1ea5d0ce9..e4a2a30f1 100644 --- a/engine-rest/src/test/java/io/nosqlbench/engine/rest/services/openapi/OpenApiLoaderTest.java +++ b/engine-rest/src/test/java/io/nosqlbench/engine/rest/services/openapi/OpenApiLoaderTest.java @@ -16,10 +16,12 @@ package io.nosqlbench.engine.rest.services.openapi; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.junit.jupiter.api.Test; public class OpenApiLoaderTest { - + private final static Logger logger = LogManager.getLogger(OpenApiLoaderTest.class); @Test public void testYamlGenerator() { String openidpath = "stargate.yaml"; @@ -28,7 +30,7 @@ public class OpenApiLoaderTest { "}\n"; String result = OpenApiLoader.generateWorkloadFromFilepath(openidpath, filterJson); - System.out.println(result); + logger.debug(result); } diff --git a/virtdata-lib-basics/src/test/java/io/nosqlbench/virtdata/library/basics/shared/from_long/to_long/TriangularStepTest.java b/virtdata-lib-basics/src/test/java/io/nosqlbench/virtdata/library/basics/shared/from_long/to_long/TriangularStepTest.java index 8a8ab8574..8887a53e5 100644 --- a/virtdata-lib-basics/src/test/java/io/nosqlbench/virtdata/library/basics/shared/from_long/to_long/TriangularStepTest.java +++ b/virtdata-lib-basics/src/test/java/io/nosqlbench/virtdata/library/basics/shared/from_long/to_long/TriangularStepTest.java @@ -34,7 +34,7 @@ public class TriangularStepTest { public void testStepExample1() { TriangularStep e1 = new TriangularStep(100, 20); int[] runLengths = this.rleStatsFor(e1, 0, 10000); - System.out.println(Arrays.toString(runLengths)); +// System.out.println(Arrays.toString(runLengths)); assertThat(IntStream.of(runLengths).min().orElseThrow()).isEqualTo(80L); assertThat(IntStream.of(runLengths).max().orElseThrow()).isEqualTo(120L); } @@ -43,7 +43,7 @@ public class TriangularStepTest { public void testStepExample2() { TriangularStep e1 = new TriangularStep(80, 10); int[] runLengths = this.rleStatsFor(e1, 0, 10000); - System.out.println(Arrays.toString(runLengths)); +// System.out.println(Arrays.toString(runLengths)); assertThat(IntStream.of(runLengths).min().orElseThrow()).isEqualTo(70L); assertThat(IntStream.of(runLengths).max().orElseThrow()).isEqualTo(90L); } @@ -101,7 +101,7 @@ public class TriangularStepTest { } for (int count : counts) { - System.out.println(count); +// System.out.println(count); histo[count-minval][FREQUENCY]++; } return histo; diff --git a/virtdata-lib-basics/src/test/java/io/nosqlbench/virtdata/library/basics/tests/long_long/SignedHashTest.java b/virtdata-lib-basics/src/test/java/io/nosqlbench/virtdata/library/basics/tests/long_long/SignedHashTest.java index 3fe902c4f..16b29b2be 100644 --- a/virtdata-lib-basics/src/test/java/io/nosqlbench/virtdata/library/basics/tests/long_long/SignedHashTest.java +++ b/virtdata-lib-basics/src/test/java/io/nosqlbench/virtdata/library/basics/tests/long_long/SignedHashTest.java @@ -17,11 +17,14 @@ package io.nosqlbench.virtdata.library.basics.tests.long_long; import io.nosqlbench.virtdata.library.basics.shared.from_long.to_long.SignedHash; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; public class SignedHashTest { + private final static Logger logger = LogManager.getLogger(SignedHashTest.class); @Test public void testFunctionalResult() { @@ -44,12 +47,13 @@ public class SignedHashTest { SignedHash hash = new SignedHash(); for (int i = 0; i < 10; i++) { long l = hash.applyAsLong(i) % 50L; - System.out.println("i=" + i + " result=" + l); + logger.debug("i=" + i + " result=" + l); + } for (int i = 0; i < 10; i++) { long l = hash.applyAsLong(i+1000000L) % 50L; - System.out.println("i=" + i + " result=" + l); + logger.debug("i=" + i + " result=" + l); } } diff --git a/virtdata-lib-basics/src/test/java/io/nosqlbench/virtdata/library/basics/tests/long_string/CombinationsTest.java b/virtdata-lib-basics/src/test/java/io/nosqlbench/virtdata/library/basics/tests/long_string/CombinationsTest.java index bffcf56d2..2e0a27a72 100644 --- a/virtdata-lib-basics/src/test/java/io/nosqlbench/virtdata/library/basics/tests/long_string/CombinationsTest.java +++ b/virtdata-lib-basics/src/test/java/io/nosqlbench/virtdata/library/basics/tests/long_string/CombinationsTest.java @@ -17,12 +17,15 @@ package io.nosqlbench.virtdata.library.basics.tests.long_string; import io.nosqlbench.virtdata.library.basics.shared.from_long.to_string.Combinations; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; public class CombinationsTest { + private final static Logger logger = LogManager.getLogger(CombinationsTest.class); @Test public void testSimplePrimes() { @@ -38,7 +41,7 @@ public class CombinationsTest { Combinations binaryByte = new Combinations("0-1;0-1;0-1;0-1;0-1;0-1;0-1;0-1;"); assertThat(binaryByte.apply(37)).isEqualTo("00100101"); for (int i = 0; i < 512; i++) { - System.out.println("i:" + i + " = b:" + binaryByte.apply(i)); + logger.debug("i:" + i + " = b:" + binaryByte.apply(i)); } } diff --git a/virtdata-lib-curves4/src/test/java/io/nosqlbench/virtdata/library/curves4/continuous/RealDistributionsConcurrencyTests.java b/virtdata-lib-curves4/src/test/java/io/nosqlbench/virtdata/library/curves4/continuous/RealDistributionsConcurrencyTests.java index a34c625cf..591ec73a7 100644 --- a/virtdata-lib-curves4/src/test/java/io/nosqlbench/virtdata/library/curves4/continuous/RealDistributionsConcurrencyTests.java +++ b/virtdata-lib-curves4/src/test/java/io/nosqlbench/virtdata/library/curves4/continuous/RealDistributionsConcurrencyTests.java @@ -18,6 +18,8 @@ package io.nosqlbench.virtdata.library.curves4.continuous; import io.nosqlbench.virtdata.core.bindings.DataMapper; import io.nosqlbench.virtdata.core.bindings.VirtData; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.junit.jupiter.api.Test; import java.util.ArrayList; @@ -30,6 +32,7 @@ import java.util.concurrent.Future; import static org.assertj.core.api.Assertions.assertThat; public class RealDistributionsConcurrencyTests { + private final static Logger logger = LogManager.getLogger(RealDistributionsConcurrencyTests.class); @Test public void testConcurrentBinomialHashValues() { @@ -73,8 +76,7 @@ public class RealDistributionsConcurrencyTests { for (int i = 0; i < futures.size(); i++) { try { results.add(futures.get(i).get()); -// System.out.println(description + ": got results for thread " + i); -// System.out.flush(); + logger.trace(description + ": got results for thread " + i); } catch (Exception e) { throw new RuntimeException(e); } @@ -83,7 +85,7 @@ public class RealDistributionsConcurrencyTests { for (int vthread = 0; vthread < threads; vthread++) { assertThat(results.get(vthread)).isEqualTo(values); - System.out.println(description + ": verified values for thread " + vthread); + logger.debug(description + ": verified values for thread " + vthread); } @@ -107,8 +109,7 @@ public class RealDistributionsConcurrencyTests { public double[] call() throws Exception { double[] output = new double[size]; DataMapper mapper = VirtData.getMapper(mapperSpec, double.class); -// System.out.println("resolved:" + mapper); -// System.out.flush(); + logger.trace("resolved:" + mapper); synchronized (signal) { signal.wait(10000); @@ -116,9 +117,9 @@ public class RealDistributionsConcurrencyTests { for (int i = 0; i < output.length; i++) { output[i] = mapper.get(i); -// if ((i % 100) == 0) { -// System.out.println("wrote t:" + slot + ", iter:" + i + ", val:" + output[i]); -// } + if ((i % 100) == 0) { + logger.trace("wrote t:" + slot + ", iter:" + i + ", val:" + output[i]); + } } return output; } diff --git a/virtdata-lib-curves4/src/test/java/io/nosqlbench/virtdata/library/curves4/continuous/RealDistributionsValuesTest.java b/virtdata-lib-curves4/src/test/java/io/nosqlbench/virtdata/library/curves4/continuous/RealDistributionsValuesTest.java index c08a20a24..caf879a28 100644 --- a/virtdata-lib-curves4/src/test/java/io/nosqlbench/virtdata/library/curves4/continuous/RealDistributionsValuesTest.java +++ b/virtdata-lib-curves4/src/test/java/io/nosqlbench/virtdata/library/curves4/continuous/RealDistributionsValuesTest.java @@ -19,6 +19,8 @@ package io.nosqlbench.virtdata.library.curves4.continuous; import io.nosqlbench.virtdata.library.curves4.continuous.long_double.Normal; import io.nosqlbench.virtdata.library.curves4.continuous.long_double.Uniform; import org.apache.commons.math4.stat.descriptive.DescriptiveStatistics; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.assertj.core.data.Offset; import org.junit.jupiter.api.Test; @@ -29,12 +31,13 @@ import java.util.function.LongToDoubleFunction; import static org.assertj.core.api.Assertions.assertThat; public class RealDistributionsValuesTest { + private final static Logger logger = LogManager.getLogger(RealDistributionsValuesTest.class); @Test public void testComputedNormal() { RunData runData = iterateMapperDouble(new Normal(10.0,2.0,"compute"), 1000000,1); - System.out.println(runData); + logger.debug(runData); assertThat(runData.getFractionalPercentile(0.5D)) .isCloseTo(10.0D, Offset.offset(0.01D)); assertThat(runData.getFractionalPercentile(0.4D)) @@ -46,7 +49,7 @@ public class RealDistributionsValuesTest { @Test public void testInterpolatedNormal() { RunData runData = iterateMapperDouble(new Normal(10.0,2.0,"interpolate"), 1000000,1); - System.out.println(runData); + logger.debug(runData); assertThat(runData.getFractionalPercentile(0.5D)) .isCloseTo(10.0D, Offset.offset(0.01D)); assertThat(runData.getFractionalPercentile(0.4D)) @@ -64,7 +67,7 @@ public class RealDistributionsValuesTest { .isCloseTo(50.0D, Offset.offset(1.0D)); assertThat(runData.getFractionalPercentile(0.78D)) .isCloseTo(78.0D, Offset.offset(1.0D)); - System.out.println(runData); + logger.debug(runData); } @Test @@ -76,7 +79,7 @@ public class RealDistributionsValuesTest { .isCloseTo(50.0D, Offset.offset(1.0D)); assertThat(runData.getFractionalPercentile(0.78D)) .isCloseTo(78.0D, Offset.offset(1.0D)); - System.out.println(runData); + logger.debug(runData); } @Test @@ -92,7 +95,7 @@ public class RealDistributionsValuesTest { assertThat(mapper.applyAsDouble(Long.MAX_VALUE)).isCloseTo(100.0D, Offset.offset(1.0D)); - System.out.println(runData); + logger.debug(runData); } private RunData iterateMapperDouble(LongToDoubleFunction mapper, int iterations, long funcstep) { diff --git a/virtdata-lib-curves4/src/test/java/io/nosqlbench/virtdata/library/curves4/discrete/IntegerDistributionsConcurrencyTest.java b/virtdata-lib-curves4/src/test/java/io/nosqlbench/virtdata/library/curves4/discrete/IntegerDistributionsConcurrencyTest.java index cbe439072..90ec8b7c0 100644 --- a/virtdata-lib-curves4/src/test/java/io/nosqlbench/virtdata/library/curves4/discrete/IntegerDistributionsConcurrencyTest.java +++ b/virtdata-lib-curves4/src/test/java/io/nosqlbench/virtdata/library/curves4/discrete/IntegerDistributionsConcurrencyTest.java @@ -19,6 +19,8 @@ package io.nosqlbench.virtdata.library.curves4.discrete; import io.nosqlbench.virtdata.core.bindings.DataMapper; import io.nosqlbench.virtdata.core.bindings.VirtData; import org.apache.commons.statistics.distribution.BinomialDistribution; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.assertj.core.data.Offset; import org.junit.jupiter.api.Test; @@ -33,6 +35,7 @@ import java.util.concurrent.Future; import static org.assertj.core.api.Assertions.assertThat; public class IntegerDistributionsConcurrencyTest { + private final static Logger logger = LogManager.getLogger(IntegerDistributionsConcurrencyTest.class); @Test public void testBinomialICDR() { @@ -109,7 +112,6 @@ public class IntegerDistributionsConcurrencyTest { try { results[i] = futures.get(i).get(); System.out.println(description + ": got results for thread " + i); - System.out.flush(); } catch (Exception e) { throw new RuntimeException(e); } @@ -125,15 +127,14 @@ public class IntegerDistributionsConcurrencyTest { for (int ithread = 0; ithread < threads; ithread++) { System.out.print(results[ithread][i] + ","); } - System.out.println(); } } boolean equal = Arrays.equals(results[vthread],values); if (!equal) { - System.out.println("not equal!"); + logger.debug("not equal!"); } assertThat(results[vthread]).isEqualTo(values); - System.out.println(description + ": verified values for thread " + vthread); + logger.debug(description + ": verified values for thread " + vthread); } @@ -157,8 +158,7 @@ public class IntegerDistributionsConcurrencyTest { public long[] call() throws Exception { long[] output = new long[size]; DataMapper mapper = VirtData.getMapper(mapperSpec, long.class); -// System.out.println("resolved:" + mapper); -// System.out.flush(); +// logger.debug("resolved:" + mapper); synchronized (signal) { signal.wait(10000); @@ -167,7 +167,7 @@ public class IntegerDistributionsConcurrencyTest { for (int i = 0; i < output.length; i++) { output[i] = mapper.get(i); // if ((i % 100) == 0) { -// System.out.println("wrote t:" + slot + ", iter:" + i + ", val:" + output[i]); +// logger.debug("wrote t:" + slot + ", iter:" + i + ", val:" + output[i]); // } } return output; diff --git a/virtdata-lib-curves4/src/test/java/io/nosqlbench/virtdata/library/curves4/discrete/IntegerDistributionsValuesTest.java b/virtdata-lib-curves4/src/test/java/io/nosqlbench/virtdata/library/curves4/discrete/IntegerDistributionsValuesTest.java index 0ded14829..4c8bc3a6e 100644 --- a/virtdata-lib-curves4/src/test/java/io/nosqlbench/virtdata/library/curves4/discrete/IntegerDistributionsValuesTest.java +++ b/virtdata-lib-curves4/src/test/java/io/nosqlbench/virtdata/library/curves4/discrete/IntegerDistributionsValuesTest.java @@ -19,6 +19,8 @@ package io.nosqlbench.virtdata.library.curves4.discrete; import io.nosqlbench.virtdata.library.curves4.continuous.long_double.Uniform; import io.nosqlbench.virtdata.library.curves4.discrete.long_long.Zipf; import org.apache.commons.math4.stat.descriptive.DescriptiveStatistics; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.assertj.core.data.Offset; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -31,13 +33,14 @@ import java.util.function.LongUnaryOperator; import static org.assertj.core.api.Assertions.assertThat; public class IntegerDistributionsValuesTest { + private final static Logger logger = LogManager.getLogger(IntegerDistributionsValuesTest.class); @Disabled @Test public void testComputedZipf() { RunData runData = iterateMapperLong(new Zipf(10000,2.0), 10000); - System.out.println(runData); + logger.debug(runData); assertThat(runData.getFractionalPercentile(0.6D)) .isCloseTo(1.0D, Offset.offset(0.0001D)); assertThat(runData.getFractionalPercentile(0.7D)) @@ -51,7 +54,7 @@ public class IntegerDistributionsValuesTest { @Test public void testInterpolatedZipf() { RunData runData = iterateMapperLong(new Zipf(10000,2.0), 10000); - System.out.println(runData); + logger.debug(runData); assertThat(runData.getFractionalPercentile(0.6D)) .isCloseTo(1.0D, Offset.offset(0.0001D)); assertThat(runData.getFractionalPercentile(0.7D)) @@ -72,7 +75,7 @@ public class IntegerDistributionsValuesTest { .isCloseTo(50.0D, Offset.offset(1.0D)); assertThat(runData.getFractionalPercentile(0.78D)) .isCloseTo(78.0D, Offset.offset(1.0D)); - System.out.println(runData); + logger.debug(runData); } @Test @@ -84,7 +87,7 @@ public class IntegerDistributionsValuesTest { .isCloseTo(50.0D, Offset.offset(1.0D)); assertThat(runData.getFractionalPercentile(0.78D)) .isCloseTo(78.0D, Offset.offset(1.0D)); - System.out.println(runData); + logger.debug(runData); } @Test @@ -127,7 +130,7 @@ public class IntegerDistributionsValuesTest { int readout = iterations/10; for (int i = 0; i < iterations; i++) { if ((i%readout)==0) { - System.out.println("i="+i+"/"+iterations); + logger.debug("i="+i+"/"+iterations); } samples[i] = mapper.applyAsDouble(i); } diff --git a/virtdata-userlibs/src/test/java/io/virtdata/IntegratedComposerLogicTest.java b/virtdata-userlibs/src/test/java/io/virtdata/IntegratedComposerLogicTest.java index ebdb9bcc9..21aa6384f 100644 --- a/virtdata-userlibs/src/test/java/io/virtdata/IntegratedComposerLogicTest.java +++ b/virtdata-userlibs/src/test/java/io/virtdata/IntegratedComposerLogicTest.java @@ -22,6 +22,8 @@ import io.nosqlbench.virtdata.library.basics.shared.from_long.to_long.Identity; import io.nosqlbench.virtdata.library.basics.shared.from_long.to_string.NumberNameToString; import io.nosqlbench.virtdata.library.basics.shared.from_long.to_string.Template; import org.apache.commons.lang3.ClassUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.junit.jupiter.api.Test; import java.util.Optional; @@ -33,6 +35,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; public class IntegratedComposerLogicTest { + private final static Logger logger = LogManager.getLogger(IntegratedComposerLogicTest.class); @Test public void testPreferredReturnType() { diff --git a/virtdata-userlibs/src/test/java/io/virtdata/IntegratedCurvesTest.java b/virtdata-userlibs/src/test/java/io/virtdata/IntegratedCurvesTest.java index 283d734d2..544b39bac 100644 --- a/virtdata-userlibs/src/test/java/io/virtdata/IntegratedCurvesTest.java +++ b/virtdata-userlibs/src/test/java/io/virtdata/IntegratedCurvesTest.java @@ -19,6 +19,8 @@ package io.virtdata; import io.nosqlbench.virtdata.core.bindings.DataMapper; import io.nosqlbench.virtdata.core.bindings.VirtData; import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.assertj.core.data.Offset; import org.junit.jupiter.api.Test; @@ -27,15 +29,14 @@ import java.util.Locale; import static org.assertj.core.api.Assertions.assertThat; -//import org.apache.commons.math4.stat.descriptive.DescriptiveStatistics; - public class IntegratedCurvesTest { + private final static Logger logger = LogManager.getLogger(IntegratedComposerLogicTest.class); @Test public void testZipf() { DataMapper mapper = VirtData.getMapper("Zipf(1000,2) -> long", long.class); RunData runData = iterateMapperLong(mapper, 10000); - System.out.println(runData); + logger.debug(runData); assertThat(runData.getStats().getPercentile(0.1d)).isCloseTo(1.0, Offset.offset(0.01d)); assertThat(runData.getStats().getPercentile(1.0d)).isCloseTo(1.0, Offset.offset(0.01d)); From 6da3798bb1f6326159ff0566dfce10609ad16797 Mon Sep 17 00:00:00 2001 From: Jonathan Shook Date: Wed, 21 Dec 2022 01:23:02 -0600 Subject: [PATCH 18/19] Update build.yml diagnostics --- .github/workflows/build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 68ffdd494..c41f09f5f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -91,6 +91,8 @@ jobs: NBDROID_NAME: ${{ secrets.NBDROID_NAME }} NBDROID_TOKEN: ${{ secrets.NBDROID_TOKEN }} run: | + set -x + find . -ls rsync -av --delete -I --exclude '_index.md' drivers/ nosqlbench-build-docs/site/content/docs/drivers rsync -av --delete -I --exclude '_index.md' bindings/ nosqlbench-build-docs/site/content/docs/bindings echo "previewdocs.nosqlbench.io" > nosqlbench-build-docs/site/staticCNAME From 301f1e2d279bdc26e14bed33e50c4f2284f3020e Mon Sep 17 00:00:00 2001 From: Jonathan Shook Date: Wed, 21 Dec 2022 13:36:12 -0600 Subject: [PATCH 19/19] added missing category for TriangularStepFunction --- .../basics/shared/from_long/to_long/TriangularStep.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/virtdata-lib-basics/src/main/java/io/nosqlbench/virtdata/library/basics/shared/from_long/to_long/TriangularStep.java b/virtdata-lib-basics/src/main/java/io/nosqlbench/virtdata/library/basics/shared/from_long/to_long/TriangularStep.java index c9bc86a9f..5597354fb 100644 --- a/virtdata-lib-basics/src/main/java/io/nosqlbench/virtdata/library/basics/shared/from_long/to_long/TriangularStep.java +++ b/virtdata-lib-basics/src/main/java/io/nosqlbench/virtdata/library/basics/shared/from_long/to_long/TriangularStep.java @@ -16,6 +16,8 @@ package io.nosqlbench.virtdata.library.basics.shared.from_long.to_long; +import io.nosqlbench.virtdata.api.annotations.Categories; +import io.nosqlbench.virtdata.api.annotations.Category; import io.nosqlbench.virtdata.api.annotations.Example; import io.nosqlbench.virtdata.api.annotations.ThreadSafeMapper; @@ -38,6 +40,7 @@ import java.util.function.LongUnaryOperator; * join the project or let us know.

*/ @ThreadSafeMapper +@Categories({Category.experimental}) public class TriangularStep implements LongUnaryOperator { private final Hash hasher = new Hash();