clarify scenario error handling logic

This commit is contained in:
Jonathan Shook 2020-07-16 09:20:30 -05:00
parent 1fe38b4394
commit 452224a201
2 changed files with 90 additions and 27 deletions

View File

@ -0,0 +1,83 @@
package io.nosqlbench.engine.core;
import io.nosqlbench.nb.api.errors.BasicError;
import org.graalvm.polyglot.PolyglotException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.script.ScriptException;
/**
* This class is meant to consolidate the error handling logic for the varios types of errors
* that may bubble up from the layers within NoSQLBench. As a layered system, some of the included
* libraries tend to layer the exceptions beyond a point of recognizability. The logic in this
* class should do the following:
*
* <ol>
* <li>Report an error in the most intelligible way to the user.</li>
* </ol>
*
* That is all. When this error handler is invoked, it is a foregone conclusion that the scenario
* is not able to continue, else the error would have been trapped and handled internal to a lower-level
* class. It is the calling exception handler's responsibility to finally shut down the scenario
* cleanly and return appropriately. Thus, <em>You should not throw errors from this class. You should only
* unwrap and explain errors, sending contents to the logfile as appropriate.</em>
*
*/
public class ScenarioErrorHandler {
private final static Logger logger = LoggerFactory.getLogger(ScenarioErrorHandler.class);
public static void handle(String script, Throwable t, boolean wantsStackTraces) {
if (t instanceof ScriptException) {
handleScriptException(script, (ScriptException) t, wantsStackTraces);
} else if (t instanceof BasicError) {
handleBasicError((BasicError) t, wantsStackTraces);
} else if (t instanceof Exception){
handleInternalError((Exception) t, wantsStackTraces);
}
}
private static void handleInternalError(Exception e, boolean wantsStackTraces) {
String prefix = "internal error: ";
if (!e.getCause().getClass().getCanonicalName().contains("io.nosqlbench")) {
prefix = "Error from driver or included library: ";
}
if (wantsStackTraces) {
logger.error(prefix + e.getMessage(),e);
} else {
logger.error(e.getMessage());
logger.error("for the full stack trace, run with --show-stacktraces");
}
}
private static void handleScriptException(String script, ScriptException e, boolean wantsStackTraces) {
Throwable cause = e.getCause();
if (cause instanceof PolyglotException) {
Throwable hostException = ((PolyglotException) cause).asHostException();
if (hostException instanceof BasicError) {
handleBasicError((BasicError)hostException, wantsStackTraces);
} else {
handle(script,hostException, wantsStackTraces);
}
} else {
if (wantsStackTraces) {
logger.error("Unknown script exception:",e);
} else {
logger.error(e.getMessage());
logger.error("for the full stack trace, run with --show-stacktraces");
}
}
}
private static void handleBasicError(BasicError e, boolean wantsStackTraces) {
if (wantsStackTraces) {
logger.error(e.getMessage(),e);
} else {
logger.error(e.getMessage());
logger.error("for the full stack trace, run with --show-stacktraces");
}
}
}

View File

@ -19,16 +19,12 @@ import com.codahale.metrics.MetricRegistry;
import com.oracle.truffle.js.scriptengine.GraalJSScriptEngine;
import io.nosqlbench.engine.core.*;
import io.nosqlbench.engine.core.metrics.PolyglotMetricRegistryBindings;
import io.nosqlbench.nb.api.errors.BasicError;
import io.nosqlbench.engine.api.extensions.ScriptingPluginInfo;
import io.nosqlbench.engine.api.metrics.ActivityMetrics;
import io.nosqlbench.engine.core.metrics.NashornMetricRegistryBindings;
import io.nosqlbench.engine.api.scripting.ScriptEnvBuffer;
import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.EnvironmentAccess;
import org.graalvm.polyglot.HostAccess;
import org.graalvm.polyglot.PolyglotAccess;
import org.graalvm.polyglot.*;
import org.slf4j.LoggerFactory;
import javax.script.*;
@ -61,17 +57,19 @@ public class Scenario implements Callable<ScenarioResult> {
private ScenarioLogger scenarioLogger;
private ScriptParams scenarioScriptParams;
private Engine engine = Engine.Graalvm;
private boolean wantsStackTraces=false;
public enum Engine {
Nashorn,
Graalvm
}
public Scenario(String name, Engine engine, String progressInterval, boolean wantsGraaljsCompatMode) {
public Scenario(String name, Engine engine, String progressInterval, boolean wantsGraaljsCompatMode, boolean wantsStackTraces) {
this.name = name;
this.engine = engine;
this.progressInterval = progressInterval;
this.wantsGraaljsCompatMode = wantsGraaljsCompatMode;
this.wantsStackTraces = wantsStackTraces;
}
public Scenario(String name, Engine engine) {
@ -218,28 +216,10 @@ public class Scenario implements Callable<ScenarioResult> {
}
System.err.flush();
System.out.flush();
} catch (ScriptException e) {
String diagname = "diag_" + System.currentTimeMillis() + ".js";
try {
Path diagFilePath = Paths.get(scenarioLogger.getLogDir(), diagname);
Files.writeString(diagFilePath, script);
} catch (Exception ignored) {
}
String errorDesc = "Script error while running scenario:" + e.toString() + ", script content is at " + diagname;
e.printStackTrace();
logger.error(errorDesc, e);
scenarioController.forceStopScenario(5000);
throw new RuntimeException("Script error while running scenario:" + e.getMessage(), e);
} catch (BasicError ue) {
logger.error(ue.getMessage());
scenarioController.forceStopScenario(5000);
throw ue;
} catch (Exception o) {
String errorDesc = "Non-Script error while running scenario:" + o.getMessage();
logger.error(errorDesc, o);
scenarioController.forceStopScenario(5000);
throw new RuntimeException("Non-Script error while running scenario:" + o.getMessage(), o);
} catch (Exception e) {
ScenarioErrorHandler.handle(script,e,wantsStackTraces);
} finally {
scenarioController.forceStopScenario(5000);
System.out.flush();
System.err.flush();
}