TINKERPOP-1644 Exposed GremlinScriptEngine metrics in Gremlin Server
Project: http://git-wip-us.apache.org/repos/asf/tinkerpop/repo Commit: http://git-wip-us.apache.org/repos/asf/tinkerpop/commit/b689deb1 Tree: http://git-wip-us.apache.org/repos/asf/tinkerpop/tree/b689deb1 Diff: http://git-wip-us.apache.org/repos/asf/tinkerpop/diff/b689deb1 Branch: refs/heads/TINKERPOP-1644 Commit: b689deb1dd1a0c745966a1ed5f8e7e3b2d543af2 Parents: 8ffa5af Author: Stephen Mallette <sp...@genoprime.com> Authored: Thu Mar 9 12:25:35 2017 -0500 Committer: Stephen Mallette <sp...@genoprime.com> Committed: Fri Mar 10 11:13:03 2017 -0500 ---------------------------------------------------------------------- CHANGELOG.asciidoc | 2 +- .../gremlin/groovy/engine/ScriptEngines.java | 7 ++ .../gremlin/server/op/session/Session.java | 27 +++++++ .../gremlin/server/util/MetricManager.java | 79 +++++++++++++++++--- .../server/util/ServerGremlinExecutor.java | 15 ++++ 5 files changed, 118 insertions(+), 12 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b689deb1/CHANGELOG.asciidoc ---------------------------------------------------------------------- diff --git a/CHANGELOG.asciidoc b/CHANGELOG.asciidoc index 3729271..a306dea 100644 --- a/CHANGELOG.asciidoc +++ b/CHANGELOG.asciidoc @@ -26,7 +26,7 @@ image::https://raw.githubusercontent.com/apache/tinkerpop/master/docs/static/ima TinkerPop 3.2.5 (Release Date: NOT OFFICIALLY RELEASED YET) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -* Added various metrics to the `GremlinGroovyScriptEngine` around script compilation. +* Added various metrics to the `GremlinGroovyScriptEngine` around script compilation and exposed them in Gremlin Server. * Moved the `caffeine` dependency down to `gremlin-groovy` and out of `gremlin-server`. * Improved script compilation in `GremlinGroovyScriptEngine to use better caching, log long compile times and prevent failed compilations from recompiling on future requests. * Script compilation is synchronised. http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b689deb1/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/engine/ScriptEngines.java ---------------------------------------------------------------------- diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/engine/ScriptEngines.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/engine/ScriptEngines.java index 7dcfc5c..6dfd87a 100644 --- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/engine/ScriptEngines.java +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/engine/ScriptEngines.java @@ -311,6 +311,13 @@ public class ScriptEngines implements AutoCloseable { } /** + * Gets an {@link GremlinScriptEngine} by its name. + */ + public GremlinScriptEngine getEngineByName(final String engineName) { + return scriptEngines.get(engineName); + } + + /** * Get the set of {@code ScriptEngine} that implement {@link DependencyManager} interface. */ private Set<DependencyManager> getDependencyManagers() { http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b689deb1/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/session/Session.java ---------------------------------------------------------------------- diff --git a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/session/Session.java b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/session/Session.java index 9b16a3b..b8cb28c 100644 --- a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/session/Session.java +++ b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/op/session/Session.java @@ -18,11 +18,16 @@ */ package org.apache.tinkerpop.gremlin.server.op.session; +import com.codahale.metrics.Metric; +import com.codahale.metrics.MetricFilter; import org.apache.tinkerpop.gremlin.groovy.engine.GremlinExecutor; +import org.apache.tinkerpop.gremlin.groovy.engine.ScriptEngines; +import org.apache.tinkerpop.gremlin.jsr223.GremlinScriptEngine; import org.apache.tinkerpop.gremlin.server.Context; import org.apache.tinkerpop.gremlin.server.GraphManager; import org.apache.tinkerpop.gremlin.server.Settings; import org.apache.tinkerpop.gremlin.server.util.LifeCycleHook; +import org.apache.tinkerpop.gremlin.server.util.MetricManager; import org.apache.tinkerpop.gremlin.server.util.ThreadFactoryUtil; import org.apache.tinkerpop.gremlin.structure.Graph; import org.slf4j.Logger; @@ -30,6 +35,7 @@ import org.slf4j.LoggerFactory; import javax.script.Bindings; import javax.script.SimpleBindings; +import java.util.Collections; import java.util.HashSet; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; @@ -92,6 +98,15 @@ public class Session { SessionOpProcessor.CONFIG_PER_GRAPH_CLOSE_TIMEOUT, SessionOpProcessor.DEFAULT_PER_GRAPH_CLOSE_TIMEOUT).toString()); this.gremlinExecutor = initializeGremlinExecutor().create(); + + settings.scriptEngines.keySet().forEach(engineName -> { + try { + gremlinExecutor.eval("1+1", engineName, Collections.emptyMap()).join(); + registerMetrics(engineName); + } catch (Exception ex) { + logger.warn(String.format("Could not initialize {} ScriptEngine as script could not be evaluated - %s", engineName), ex); + } + }); } public GremlinExecutor getGremlinExecutor() { @@ -207,6 +222,10 @@ public class Session { executor.shutdownNow(); sessions.remove(session); + + // once a session is dead release the gauges in the registry for it + MetricManager.INSTANCE.getRegistry().removeMatching((s, metric) -> s.contains(session)); + logger.info("Session {} closed", session); } @@ -244,4 +263,12 @@ public class Session { return gremlinExecutorBuilder; } + + private void registerMetrics(final String engineName) { + final ScriptEngines scriptEngines = gremlinExecutor.getScriptEngines(); + final GremlinScriptEngine engine = null == scriptEngines ? + gremlinExecutor.getScriptEngineManager().getEngineByName(engineName) : + scriptEngines.getEngineByName(engineName); + MetricManager.INSTANCE.registerGremlinScriptEngineMetrics(engine, "session", session, "class-cache"); + } } http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b689deb1/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/util/MetricManager.java ---------------------------------------------------------------------- diff --git a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/util/MetricManager.java b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/util/MetricManager.java index 2867aa7..06179d2 100644 --- a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/util/MetricManager.java +++ b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/util/MetricManager.java @@ -33,6 +33,9 @@ import com.codahale.metrics.ganglia.GangliaReporter; import com.codahale.metrics.graphite.Graphite; import com.codahale.metrics.graphite.GraphiteReporter; import info.ganglia.gmetric4j.gmetric.GMetric; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.tinkerpop.gremlin.groovy.jsr223.GremlinGroovyScriptEngine; +import org.apache.tinkerpop.gremlin.jsr223.GremlinScriptEngine; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -66,17 +69,16 @@ public enum MetricManager { private GraphiteReporter graphiteReporter = null; /** - * Return the Titan Metrics registry. + * Return the {@code MetricsRegistry}. * - * @return the single {@code MetricRegistry} used for all of Titan's Metrics - * monitoring + * @return the single {@code MetricRegistry} used for all of monitoring */ public MetricRegistry getRegistry() { return registry; } /** - * Create a {@link ConsoleReporter} attached to the Titan Metrics registry. + * Create a {@link ConsoleReporter} attached to the {@code MetricsRegistry}. * * @param reportIntervalInMS milliseconds to wait between dumping metrics to the console */ @@ -106,7 +108,7 @@ public enum MetricManager { } /** - * Create a {@link CsvReporter} attached to the Titan Metrics registry. + * Create a {@link CsvReporter} attached to the {@code MetricsRegistry}. * <p/> * The {@code output} argument must be non-null but need not exist. If it * doesn't already exist, this method attempts to create it by calling @@ -153,7 +155,7 @@ public enum MetricManager { } /** - * Create a {@link JmxReporter} attached to the Titan Metrics registry. + * Create a {@link JmxReporter} attached to the {@code MetricsRegistry}. * <p/> * If {@code domain} or {@code agentId} is null, then Metrics's uses its own * internal default value(s). @@ -210,7 +212,7 @@ public enum MetricManager { } /** - * Create a {@link Slf4jReporter} attached to the Titan Metrics registry. + * Create a {@link Slf4jReporter} attached to the {@code MetricsRegistry}. * <p/> * If {@code loggerName} is null, or if it is non-null but * LoggerFactory.getLogger(loggerName) returns null, then Metrics's @@ -257,7 +259,7 @@ public enum MetricManager { } /** - * Create a {@link GangliaReporter} attached to the Titan Metrics registry. + * Create a {@link GangliaReporter} attached to the {@code MetricsRegistry}. * <p/> * {@code groupOrHost} and {@code addressingMode} must be non-null. The * remaining non-primitive arguments may be null. If {@code protocol31} is @@ -321,7 +323,7 @@ public enum MetricManager { } /** - * Create a {@link GraphiteReporter} attached to the Titan Metrics registry. + * Create a {@link GraphiteReporter} attached to the {@code MetricsRegistry}. * <p/> * If {@code prefix} is null, then Metrics's internal default prefix is used * (empty string at the time this comment was written). @@ -367,8 +369,7 @@ public enum MetricManager { } /** - * Remove all Titan Metrics reporters previously configured through the - * {@code add*} methods on this class. + * Remove all reporters previously configured through the {@code add*} methods on this class. */ public synchronized void removeAllReporters() { removeConsoleReporter(); @@ -418,4 +419,60 @@ public enum MetricManager { public Histogram getHistogram(final String prefix, final String... names) { return getRegistry().histogram(MetricRegistry.name(prefix, names)); } + + /** + * Registers metrics from a {@link GremlinScriptEngine}. At this point, this only works for the + * {@link GremlinGroovyScriptEngine} as it is the only one that collects metrics at this point. As the + * {@link GremlinScriptEngine} implementations achieve greater parity these metrics will get expanded. + */ + public void registerGremlinScriptEngineMetrics(final GremlinScriptEngine engine, final String... prefix) { + if (engine instanceof GremlinGroovyScriptEngine) { + final GremlinGroovyScriptEngine gremlinGroovyScriptEngine = (GremlinGroovyScriptEngine) engine; + MetricManager.INSTANCE.getRegistry().register( + MetricRegistry.name(GremlinGroovyScriptEngine.class, ArrayUtils.add(prefix, "long-run-compilation-count")), + (Gauge<Long>) gremlinGroovyScriptEngine::getClassCacheLongRunCompilationCount); + MetricManager.INSTANCE.getRegistry().register( + MetricRegistry.name(GremlinGroovyScriptEngine.class, ArrayUtils.add(prefix, "estimated-size")), + (Gauge<Long>) gremlinGroovyScriptEngine::getClassCacheEstimatedSize); + MetricManager.INSTANCE.getRegistry().register( + MetricRegistry.name(GremlinGroovyScriptEngine.class, ArrayUtils.add(prefix, "average-load-penalty")), + (Gauge<Double>) gremlinGroovyScriptEngine::getClassCacheAverageLoadPenalty); + MetricManager.INSTANCE.getRegistry().register( + MetricRegistry.name(GremlinGroovyScriptEngine.class, ArrayUtils.add(prefix, "eviction-count")), + (Gauge<Long>) gremlinGroovyScriptEngine::getClassCacheEvictionCount); + MetricManager.INSTANCE.getRegistry().register( + MetricRegistry.name(GremlinGroovyScriptEngine.class, ArrayUtils.add(prefix, "eviction-weight")), + (Gauge<Long>) gremlinGroovyScriptEngine::getClassCacheEvictionWeight); + MetricManager.INSTANCE.getRegistry().register( + MetricRegistry.name(GremlinGroovyScriptEngine.class, ArrayUtils.add(prefix, "hit-count")), + (Gauge<Long>) gremlinGroovyScriptEngine::getClassCacheHitCount); + MetricManager.INSTANCE.getRegistry().register( + MetricRegistry.name(GremlinGroovyScriptEngine.class, ArrayUtils.add(prefix, "hit-rate")), + (Gauge<Double>) gremlinGroovyScriptEngine::getClassCacheHitRate); + MetricManager.INSTANCE.getRegistry().register( + MetricRegistry.name(GremlinGroovyScriptEngine.class, ArrayUtils.add(prefix, "load-count")), + (Gauge<Long>) gremlinGroovyScriptEngine::getClassCacheLoadCount); + MetricManager.INSTANCE.getRegistry().register( + MetricRegistry.name(GremlinGroovyScriptEngine.class, ArrayUtils.add(prefix, "load-failure-count")), + (Gauge<Long>) gremlinGroovyScriptEngine::getClassCacheLoadFailureCount); + MetricManager.INSTANCE.getRegistry().register( + MetricRegistry.name(GremlinGroovyScriptEngine.class, ArrayUtils.add(prefix, "load-failure-rate")), + (Gauge<Double>) gremlinGroovyScriptEngine::getClassCacheLoadFailureRate); + MetricManager.INSTANCE.getRegistry().register( + MetricRegistry.name(GremlinGroovyScriptEngine.class, ArrayUtils.add(prefix, "load-success-count")), + (Gauge<Long>) gremlinGroovyScriptEngine::getClassCacheLoadSuccessCount); + MetricManager.INSTANCE.getRegistry().register( + MetricRegistry.name(GremlinGroovyScriptEngine.class, ArrayUtils.add(prefix, "miss-count")), + (Gauge<Long>) gremlinGroovyScriptEngine::getClassCacheMissCount); + MetricManager.INSTANCE.getRegistry().register( + MetricRegistry.name(GremlinGroovyScriptEngine.class, ArrayUtils.add(prefix, "miss-rate")), + (Gauge<Double>) gremlinGroovyScriptEngine::getClassCacheMissRate); + MetricManager.INSTANCE.getRegistry().register( + MetricRegistry.name(GremlinGroovyScriptEngine.class, ArrayUtils.add(prefix, "request-count")), + (Gauge<Long>) gremlinGroovyScriptEngine::getClassCacheRequestCount); + MetricManager.INSTANCE.getRegistry().register( + MetricRegistry.name(GremlinGroovyScriptEngine.class, ArrayUtils.add(prefix, "total-load-time")), + (Gauge<Long>) gremlinGroovyScriptEngine::getClassCacheTotalLoadTime); + } + } } http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/b689deb1/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/util/ServerGremlinExecutor.java ---------------------------------------------------------------------- diff --git a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/util/ServerGremlinExecutor.java b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/util/ServerGremlinExecutor.java index b9b7280..d66e0e8 100644 --- a/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/util/ServerGremlinExecutor.java +++ b/gremlin-server/src/main/java/org/apache/tinkerpop/gremlin/server/util/ServerGremlinExecutor.java @@ -18,7 +18,11 @@ */ package org.apache.tinkerpop.gremlin.server.util; +import com.codahale.metrics.Gauge; import org.apache.tinkerpop.gremlin.groovy.engine.GremlinExecutor; +import org.apache.tinkerpop.gremlin.groovy.engine.ScriptEngines; +import org.apache.tinkerpop.gremlin.groovy.jsr223.GremlinGroovyScriptEngine; +import org.apache.tinkerpop.gremlin.jsr223.GremlinScriptEngine; import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource; import org.apache.tinkerpop.gremlin.server.Channelizer; import org.apache.tinkerpop.gremlin.server.GraphManager; @@ -39,6 +43,8 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ThreadFactory; import java.util.stream.Collectors; +import static com.codahale.metrics.MetricRegistry.name; + /** * The core of script execution in Gremlin Server. Given {@link Settings} and optionally other arguments, this * class will construct a {@link GremlinExecutor} to be used by Gremlin Server. A typical usage would be to @@ -152,6 +158,7 @@ public class ServerGremlinExecutor<T extends ScheduledExecutorService> { settings.scriptEngines.keySet().forEach(engineName -> { try { gremlinExecutor.eval("1+1", engineName, Collections.emptyMap()).join(); + registerMetrics(engineName); } catch (Exception ex) { logger.warn(String.format("Could not initialize {} ScriptEngine as script could not be evaluated - %s", engineName), ex); } @@ -179,6 +186,14 @@ public class ServerGremlinExecutor<T extends ScheduledExecutorService> { .collect(Collectors.toList()); } + private void registerMetrics(final String engineName) { + final ScriptEngines scriptEngines = gremlinExecutor.getScriptEngines(); + final GremlinScriptEngine engine = null == scriptEngines ? + gremlinExecutor.getScriptEngineManager().getEngineByName(engineName) : + scriptEngines.getEngineByName(engineName); + MetricManager.INSTANCE.registerGremlinScriptEngineMetrics(engine, "sessionless", "class-cache"); + } + public void addHostOption(final String key, final Object value) { hostOptions.put(key, value); }