TINKERPOP-1644 Use Caffeine cache
Project: http://git-wip-us.apache.org/repos/asf/tinkerpop/repo Commit: http://git-wip-us.apache.org/repos/asf/tinkerpop/commit/18778e40 Tree: http://git-wip-us.apache.org/repos/asf/tinkerpop/tree/18778e40 Diff: http://git-wip-us.apache.org/repos/asf/tinkerpop/diff/18778e40 Branch: refs/heads/TINKERPOP-1644 Commit: 18778e405981523355319fa56bd04fd6cc07b845 Parents: deb6828 Author: BrynCooke <brynco...@gmail.com> Authored: Wed Mar 8 12:24:46 2017 +0000 Committer: Stephen Mallette <sp...@genoprime.com> Committed: Fri Mar 10 11:12:36 2017 -0500 ---------------------------------------------------------------------- gremlin-groovy/pom.xml | 5 ++ .../jsr223/GremlinGroovyScriptEngine.java | 75 +++++++++++--------- 2 files changed, 48 insertions(+), 32 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/18778e40/gremlin-groovy/pom.xml ---------------------------------------------------------------------- diff --git a/gremlin-groovy/pom.xml b/gremlin-groovy/pom.xml index 511952a..474b458 100644 --- a/gremlin-groovy/pom.xml +++ b/gremlin-groovy/pom.xml @@ -70,6 +70,11 @@ limitations under the License. <artifactId>jbcrypt</artifactId> <version>0.4</version> </dependency> + <dependency> + <groupId>com.github.ben-manes.caffeine</groupId> + <artifactId>caffeine</artifactId> + <version>2.3.1</version> + </dependency> <!-- TEST --> <dependency> <groupId>org.slf4j</groupId> http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/18778e40/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngine.java ---------------------------------------------------------------------- diff --git a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngine.java b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngine.java index 67275f8..51850ad 100644 --- a/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngine.java +++ b/gremlin-groovy/src/main/java/org/apache/tinkerpop/gremlin/groovy/jsr223/GremlinGroovyScriptEngine.java @@ -18,6 +18,9 @@ */ package org.apache.tinkerpop.gremlin.groovy.jsr223; +import com.github.benmanes.caffeine.cache.CacheLoader; +import com.github.benmanes.caffeine.cache.Caffeine; +import com.github.benmanes.caffeine.cache.LoadingCache; import groovy.grape.Grape; import groovy.lang.Binding; import groovy.lang.Closure; @@ -80,6 +83,8 @@ import java.util.Map; import java.util.ServiceLoader; import java.util.Set; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicLong; import java.util.regex.Pattern; @@ -154,17 +159,44 @@ public class GremlinGroovyScriptEngine extends GroovyScriptEngineImpl } }; + private GremlinGroovyClassLoader loader; + /** * Script to generated Class map. */ - private ManagedConcurrentValueMap<String, Future<Class>> classMap = new ManagedConcurrentValueMap<>(ReferenceBundle.getSoftBundle()); + private LoadingCache<String, Future<Class>> classMap = Caffeine.newBuilder().softValues().build(new CacheLoader<String, Future<Class>>() { + @Override + public Future<Class> load(String script) throws Exception { + long start = System.currentTimeMillis(); + try { + return CompletableFuture.supplyAsync(() -> loader.parseClass(script, generateScriptName()), command -> command.run()); + } catch (Exception e) { + if (e.getCause() instanceof CompilationFailedException) { + long finish = System.currentTimeMillis(); + log.error("Script compilation FAILED {} took {}ms {}", script, finish - start, e.getCause()); + throw (CompilationFailedException) e.getCause(); + } else { + throw new AssertionError("Unexpected exception when compiling script", e); + } + } finally { + long time = System.currentTimeMillis() - start; + if (time > 5000) { + //We warn if a script took longer than a few seconds. Repeatedly seeing these warnings is a sign that something is wrong. + //Scripts with a large numbers of parameters often trigger this and should be avoided. + log.warn("Script compilation {} took {}ms", script, time); + } else { + log.debug("Script compilation {} took {}ms", script, time); + } + } + } + + }); /** * Global closures map - this is used to simulate a single global functions namespace */ private ManagedConcurrentValueMap<String, Closure> globalClosures = new ManagedConcurrentValueMap<>(ReferenceBundle.getHardBundle()); - private GremlinGroovyClassLoader loader; private AtomicLong counter = new AtomicLong(0L); @@ -420,7 +452,7 @@ public class GremlinGroovyScriptEngine extends GroovyScriptEngineImpl // must clear the local cache here because the classloader has been reset. therefore, classes previously // referenced before that might not have evaluated might cleanly evaluate now. - classMap.clear(); + classMap.invalidateAll(); globalClosures.clear(); final Set<Artifact> toReuse = new HashSet<>(artifactsToUse); @@ -535,41 +567,20 @@ public class GremlinGroovyScriptEngine extends GroovyScriptEngineImpl } Class getScriptClass(final String script) throws CompilationFailedException { - Future<Class> clazz = classMap.get(script); - - long start = System.currentTimeMillis(); try { - if (clazz != null) { - return clazz.get(); - } - - clazz = CompletableFuture.supplyAsync(() -> loader.parseClass(script, generateScriptName())); - classMap.put(script, clazz); - return clazz.get(); - - } catch (Exception e) { - if (e.getCause() instanceof CompilationFailedException) { - long finish = System.currentTimeMillis(); - log.error("Script compilation FAILED {} took {}ms {}", script, finish - start, e.getCause()); - throw (CompilationFailedException) e.getCause(); - } else { - throw new AssertionError("Unexpected exception when compiling script", e); - } - } finally { - long time = System.currentTimeMillis() - start; - if (time > 5000) { - //We warn if a script took longer than a few seconds. Repeatedly seeing these warnings is a sign that something is wrong. - //Scripts with a large numbers of parameters often trigger this and should be avoided. - log.warn("Script compilation {} took {}ms", script, time); - } else { - log.debug("Script compilation {} took {}ms", script, time); - } + return classMap.get(script).get(); + } catch (ExecutionException e) { + throw ((CompilationFailedException)e.getCause()); + } catch (InterruptedException e) { + //This should never happen as the future should completed before it is returned to the us. + throw new AssertionError(); } + } boolean isCached(final String script) { - return classMap.get(script) != null; + return classMap.getIfPresent(script) != null; } Object eval(final Class scriptClass, final ScriptContext context) throws ScriptException {