Skip to content

Commit

Permalink
Scripting: Context script cache unlimited compile (elastic#53769)
Browse files Browse the repository at this point in the history
* Adds "unlimited" compilation rate for context script caches
* `script.context.${CONTEXT}.max_compilations_rate` = `unlimited`
  disables compilation rate limiting for `${CONTEXT}`'s script
  cache

Refs: elastic#50152
  • Loading branch information
stu-elastic committed Mar 20, 2020
1 parent 879e26e commit d6dc974
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 8 deletions.
11 changes: 6 additions & 5 deletions server/src/main/java/org/elasticsearch/script/ScriptCache.java
Expand Up @@ -40,14 +40,16 @@ public class ScriptCache {

private static final Logger logger = LogManager.getLogger(ScriptService.class);

static final Tuple<Integer, TimeValue> UNLIMITED_COMPILATION_RATE = new Tuple<>(0, TimeValue.ZERO);

private final Cache<CacheKey, Object> cache;
private final ScriptMetrics scriptMetrics;

private final Object lock = new Object();

// Mutable fields
private long lastInlineCompileTime;
private double scriptsPerTimeWindow;
// Mutable fields, visible for tests
long lastInlineCompileTime;
double scriptsPerTimeWindow;

// Cache settings or derived from settings
final int cacheSize;
Expand Down Expand Up @@ -150,8 +152,7 @@ public ScriptStats stats() {
* is discarded - there can never be more water in the bucket than the size of the bucket.
*/
void checkCompilationLimit() {
if (rate.v1() == 0 && rate.v2().getNanos() == 0) {
// unlimited
if (rate.equals(UNLIMITED_COMPILATION_RATE)) {
return;
}

Expand Down
Expand Up @@ -129,10 +129,16 @@ public class ScriptService implements Closeable, ClusterStateApplier {
key -> Setting.positiveTimeSetting(key, SCRIPT_GENERAL_CACHE_EXPIRE_SETTING, TimeValue.timeValueMillis(0),
Property.NodeScope, Property.Dynamic));

// Unlimited compilation rate for context-specific script caches
static final String UNLIMITED_COMPILATION_RATE_KEY = "unlimited";

public static final Setting.AffixSetting<Tuple<Integer, TimeValue>> SCRIPT_MAX_COMPILATIONS_RATE_SETTING =
Setting.affixKeySetting(CONTEXT_PREFIX,
"max_compilations_rate",
key -> new Setting<>(key, "75/5m", MAX_COMPILATION_RATE_FUNCTION, Property.NodeScope, Property.Dynamic));
key -> new Setting<>(key, "75/5m",
(String value) -> value.equals(UNLIMITED_COMPILATION_RATE_KEY) ? ScriptCache.UNLIMITED_COMPILATION_RATE:
MAX_COMPILATION_RATE_FUNCTION.apply(value),
Property.NodeScope, Property.Dynamic));

private static final Tuple<Integer, TimeValue> SCRIPT_COMPILATION_RATE_ZERO = new Tuple<>(0, TimeValue.ZERO);

Expand Down
Expand Up @@ -52,4 +52,17 @@ public void testCompilationCircuitBreaking() throws Exception {
cache.checkCompilationLimit();
}
}

public void testUnlimitedCompilationRate() {
final Integer size = ScriptService.SCRIPT_GENERAL_CACHE_SIZE_SETTING.get(Settings.EMPTY);
final TimeValue expire = ScriptService.SCRIPT_GENERAL_CACHE_EXPIRE_SETTING.get(Settings.EMPTY);
ScriptCache cache = new ScriptCache(size, expire, ScriptCache.UNLIMITED_COMPILATION_RATE);
long lastInlineCompileTime = cache.lastInlineCompileTime;
double scriptsPerTimeWindow = cache.scriptsPerTimeWindow;
for(int i=0; i < 3000; i++) {
cache.checkCompilationLimit();
assertEquals(lastInlineCompileTime, cache.lastInlineCompileTime);
assertEquals(scriptsPerTimeWindow, cache.scriptsPerTimeWindow, 0.0); // delta of 0.0 because it should never change
}
}
}
Expand Up @@ -451,6 +451,24 @@ public void testCacheHolderContextConstructor() {
assertEquals(zero, holder.contextCache.get("baz").get().rate);
}

public void testCompilationRateUnlimitedContextOnly() throws IOException {
IllegalArgumentException illegal = expectThrows(IllegalArgumentException.class, () -> {
buildScriptService(Settings.builder()
.put(SCRIPT_GENERAL_MAX_COMPILATIONS_RATE_SETTING.getKey(), ScriptService.UNLIMITED_COMPILATION_RATE_KEY)
.build());
});
assertEquals("parameter must contain a positive integer and a timevalue, i.e. 10/1m, but was [unlimited]", illegal.getMessage());

// Should not throw.
buildScriptService(Settings.builder()
.put(SCRIPT_MAX_COMPILATIONS_RATE_SETTING.getConcreteSettingForNamespace("ingest").getKey(),
ScriptService.UNLIMITED_COMPILATION_RATE_KEY)
.put(SCRIPT_MAX_COMPILATIONS_RATE_SETTING.getConcreteSettingForNamespace("field").getKey(),
ScriptService.UNLIMITED_COMPILATION_RATE_KEY)
.put(SCRIPT_GENERAL_MAX_COMPILATIONS_RATE_SETTING.getKey(), ScriptService.USE_CONTEXT_RATE_KEY)
.build());
}

public void testCacheHolderChangeSettings() {
String fooCompilationRate = "77/5m";
String barCompilationRate = "78/6m";
Expand All @@ -460,7 +478,7 @@ public void testCacheHolderChangeSettings() {
Settings s = Settings.builder()
.put(SCRIPT_GENERAL_MAX_COMPILATIONS_RATE_SETTING.getKey(), compilationRate)
.build();
Set<String> contexts = new HashSet<>(Arrays.asList("foo", "bar", "baz"));
Set<String> contexts = new HashSet<>(Arrays.asList("foo", "bar", "baz", "qux"));
ScriptService.CacheHolder holder = new ScriptService.CacheHolder(s, contexts, true);

assertNotNull(holder.general);
Expand All @@ -471,16 +489,19 @@ public void testCacheHolderChangeSettings() {
.put(SCRIPT_GENERAL_MAX_COMPILATIONS_RATE_SETTING.getKey(), ScriptService.USE_CONTEXT_RATE_KEY)
.put(SCRIPT_MAX_COMPILATIONS_RATE_SETTING.getConcreteSettingForNamespace("foo").getKey(), fooCompilationRate)
.put(SCRIPT_MAX_COMPILATIONS_RATE_SETTING.getConcreteSettingForNamespace("bar").getKey(), barCompilationRate)
.put(SCRIPT_MAX_COMPILATIONS_RATE_SETTING.getConcreteSettingForNamespace("qux").getKey(),
ScriptService.UNLIMITED_COMPILATION_RATE_KEY)
.build()
);

assertNull(holder.general);
assertNotNull(holder.contextCache);
assertEquals(3, holder.contextCache.size());
assertEquals(4, holder.contextCache.size());
assertEquals(contexts, holder.contextCache.keySet());

assertEquals(ScriptService.MAX_COMPILATION_RATE_FUNCTION.apply(fooCompilationRate), holder.contextCache.get("foo").get().rate);
assertEquals(ScriptService.MAX_COMPILATION_RATE_FUNCTION.apply(barCompilationRate), holder.contextCache.get("bar").get().rate);
assertEquals(ScriptCache.UNLIMITED_COMPILATION_RATE, holder.contextCache.get("qux").get().rate);
assertEquals(ScriptService.SCRIPT_MAX_COMPILATIONS_RATE_SETTING.getDefault(Settings.EMPTY),
holder.contextCache.get("baz").get().rate);

Expand Down

0 comments on commit d6dc974

Please sign in to comment.