deduplicate JS scripts in JsScriptEngine
This commit is contained in:
parent
79cf2ffa7f
commit
142a422b7a
@ -22,6 +22,7 @@ import delight.nashornsandbox.NashornSandbox;
|
|||||||
import delight.nashornsandbox.NashornSandboxes;
|
import delight.nashornsandbox.NashornSandboxes;
|
||||||
import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
|
import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
import javax.annotation.PreDestroy;
|
import javax.annotation.PreDestroy;
|
||||||
@ -42,9 +43,10 @@ public abstract class AbstractNashornJsSandboxService implements JsSandboxServic
|
|||||||
private ScriptEngine engine;
|
private ScriptEngine engine;
|
||||||
private ExecutorService monitorExecutorService;
|
private ExecutorService monitorExecutorService;
|
||||||
|
|
||||||
private Map<UUID, String> functionsMap = new ConcurrentHashMap<>();
|
private final Map<UUID, String> functionsMap = new ConcurrentHashMap<>();
|
||||||
|
private final Map<UUID,AtomicInteger> blackListedFunctions = new ConcurrentHashMap<>();
|
||||||
private Map<UUID,AtomicInteger> blackListedFunctions = new ConcurrentHashMap<>();
|
private final Map<String, Pair<UUID, AtomicInteger>> scriptToId = new ConcurrentHashMap<>();
|
||||||
|
private final Map<UUID, AtomicInteger> scriptIdToCount = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void init() {
|
public void init() {
|
||||||
@ -78,8 +80,12 @@ public abstract class AbstractNashornJsSandboxService implements JsSandboxServic
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ListenableFuture<UUID> eval(JsScriptType scriptType, String scriptBody, String... argNames) {
|
public ListenableFuture<UUID> eval(JsScriptType scriptType, String scriptBody, String... argNames) {
|
||||||
UUID scriptId = UUID.randomUUID();
|
Pair<UUID, AtomicInteger> deduplicated = deduplicate(scriptType, scriptBody);
|
||||||
String functionName = "invokeInternal_" + scriptId.toString().replace('-','_');
|
UUID scriptId = deduplicated.getLeft();
|
||||||
|
AtomicInteger duplicateCount = deduplicated.getRight();
|
||||||
|
|
||||||
|
if(duplicateCount.compareAndSet(0, 1)) {
|
||||||
|
String functionName = "invokeInternal_" + scriptId.toString().replace('-', '_');
|
||||||
String jsScript = generateJsScript(scriptType, functionName, scriptBody, argNames);
|
String jsScript = generateJsScript(scriptType, functionName, scriptBody, argNames);
|
||||||
try {
|
try {
|
||||||
if (useJsSandbox()) {
|
if (useJsSandbox()) {
|
||||||
@ -89,9 +95,13 @@ public abstract class AbstractNashornJsSandboxService implements JsSandboxServic
|
|||||||
}
|
}
|
||||||
functionsMap.put(scriptId, functionName);
|
functionsMap.put(scriptId, functionName);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
duplicateCount.decrementAndGet();
|
||||||
log.warn("Failed to compile JS script: {}", e.getMessage(), e);
|
log.warn("Failed to compile JS script: {}", e.getMessage(), e);
|
||||||
return Futures.immediateFailedFuture(e);
|
return Futures.immediateFailedFuture(e);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
duplicateCount.incrementAndGet();
|
||||||
|
}
|
||||||
return Futures.immediateFuture(scriptId);
|
return Futures.immediateFuture(scriptId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,6 +132,13 @@ public abstract class AbstractNashornJsSandboxService implements JsSandboxServic
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ListenableFuture<Void> release(UUID scriptId) {
|
public ListenableFuture<Void> release(UUID scriptId) {
|
||||||
|
AtomicInteger count = scriptIdToCount.get(scriptId);
|
||||||
|
if(count != null) {
|
||||||
|
if(count.decrementAndGet() > 0) {
|
||||||
|
return Futures.immediateFuture(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
String functionName = functionsMap.get(scriptId);
|
String functionName = functionsMap.get(scriptId);
|
||||||
if (functionName != null) {
|
if (functionName != null) {
|
||||||
try {
|
try {
|
||||||
@ -156,4 +173,16 @@ public abstract class AbstractNashornJsSandboxService implements JsSandboxServic
|
|||||||
throw new RuntimeException("No script factory implemented for scriptType: " + scriptType);
|
throw new RuntimeException("No script factory implemented for scriptType: " + scriptType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Pair<UUID, AtomicInteger> deduplicate(JsScriptType scriptType, String scriptBody) {
|
||||||
|
Pair<UUID, AtomicInteger> precomputed = Pair.of(UUID.randomUUID(), new AtomicInteger());
|
||||||
|
|
||||||
|
Pair<UUID, AtomicInteger> pair = scriptToId.computeIfAbsent(deduplicateKey(scriptType, scriptBody), i -> precomputed);
|
||||||
|
AtomicInteger duplicateCount = scriptIdToCount.computeIfAbsent(pair.getLeft(), i -> pair.getRight());
|
||||||
|
return Pair.of(pair.getLeft(), duplicateCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String deduplicateKey(JsScriptType scriptType, String scriptBody) {
|
||||||
|
return scriptType + "_" + scriptBody;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,7 +45,7 @@ public class RuleNodeJsScriptEngine implements org.thingsboard.rule.engine.api.S
|
|||||||
try {
|
try {
|
||||||
this.scriptId = this.sandboxService.eval(JsScriptType.RULE_NODE_SCRIPT, script, argNames).get();
|
this.scriptId = this.sandboxService.eval(JsScriptType.RULE_NODE_SCRIPT, script, argNames).get();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new IllegalArgumentException("Can't compile script: " + e.getMessage());
|
throw new IllegalArgumentException("Can't compile script: " + e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user