Scripting: Context script cache unlimited compile (#53769) (#53899)

* 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: #50152
This commit is contained in:
Stuart Tettemer 2020-03-20 15:14:30 -06:00 committed by GitHub
parent 1f3de2fa7e
commit ac575b68a9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 49 additions and 8 deletions

View File

@ -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;
@ -150,8 +152,7 @@ public class ScriptCache {
* 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;
}

View File

@ -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);

View File

@ -52,4 +52,17 @@ public class ScriptCacheTests extends ESTestCase {
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
}
}
}

View File

@ -451,6 +451,24 @@ public class ScriptServiceTests extends ESTestCase {
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";
@ -460,7 +478,7 @@ public class ScriptServiceTests extends ESTestCase {
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);
@ -471,16 +489,19 @@ public class ScriptServiceTests extends ESTestCase {
.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);