Skip to content

Commit

Permalink
Opt date valued script fields out of rate limit (#61238)
Browse files Browse the repository at this point in the history
This excuses script fields from the script compilation rate limiting.
It'd be fairly sad to prevent mapping updates because of the rate limit.
And we don't expect folks to add a zillion fields. On the other hand,
once we allow these scripts on the `_search` request we might indeed
want them to be considered in the limit. But we don't support that yet
and we can deal with that when we get there.
  • Loading branch information
nik9000 authored Aug 18, 2020
1 parent b5e8524 commit 40e3aec
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public class ScriptCache {

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

static final CompilationRate UNLIMITED_COMPILATION_RATE = new CompilationRate(0, TimeValue.ZERO);
public static final CompilationRate UNLIMITED_COMPILATION_RATE = new CompilationRate(0, TimeValue.ZERO);

private final Cache<CacheKey, Object> cache;
private final ScriptMetrics scriptMetrics;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,42 @@
import org.apache.lucene.index.LeafReaderContext;
import org.elasticsearch.index.fielddata.ScriptDocValues;
import org.elasticsearch.script.AggregationScript;
import org.elasticsearch.script.ScriptCache;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.search.lookup.LeafSearchLookup;
import org.elasticsearch.search.lookup.SearchLookup;

import java.util.Map;

import static org.elasticsearch.common.unit.TimeValue.timeValueMillis;

/**
* Abstract base for scripts to execute to build scripted fields. Inspired by
* {@link AggregationScript} but hopefully with less historical baggage.
*/
public abstract class AbstractScriptFieldScript {
public static <F> ScriptContext<F> newContext(String name, Class<F> factoryClass) {
return new ScriptContext<F>(
name + "_script_field",
factoryClass,
/*
* In an ideal world we wouldn't need the script cache at all
* because we have a hard reference to the script. The trouble
* is that we compile the scripts a few times when performing
* a mapping update. This is unfortunate, but we rely on the
* cache to speed this up.
*/
100,
timeValueMillis(0),
/*
* Disable compilation rate limits for scripted fields so we
* don't prevent mapping updates because we've performed too
* many recently. That'd just be lame.
*/
ScriptCache.UNLIMITED_COMPILATION_RATE.asTuple()
);
}

private final Map<String, Object> params;
private final LeafSearchLookup leafSearchLookup;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import java.util.Map;

public abstract class DateScriptFieldScript extends AbstractLongScriptFieldScript {
public static final ScriptContext<Factory> CONTEXT = new ScriptContext<>("date_script_field", Factory.class);
public static final ScriptContext<Factory> CONTEXT = newContext("date", Factory.class);

static List<Whitelist> whitelist() {
return List.of(WhitelistLoader.loadFromResourceFiles(RuntimeFieldsPainlessExtension.class, "date_whitelist.txt"));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

package org.elasticsearch.xpack.runtimefields;

import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.script.ScriptType;
import org.elasticsearch.test.ESTestCase;

import java.io.IOException;
import java.util.Map;

public class DateScriptFieldScriptTests extends ESTestCase {
public static final DateScriptFieldScript.Factory DUMMY = (params, lookup, formatter) -> ctx -> new DateScriptFieldScript(
params,
lookup,
formatter,
ctx
) {
@Override
public void execute() {
new DateScriptFieldScript.Millis(this).millis(1595431354874L);
}
};

public void testRateLimitingDisabled() throws IOException {
try (ScriptService scriptService = TestScriptEngine.scriptService(DateScriptFieldScript.CONTEXT, DUMMY)) {
for (int i = 0; i < 1000; i++) {
scriptService.compile(new Script(ScriptType.INLINE, "test", "test_" + i, Map.of()), DateScriptFieldScript.CONTEXT);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

package org.elasticsearch.xpack.runtimefields;

import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.script.ScriptContext;
import org.elasticsearch.script.ScriptEngine;
import org.elasticsearch.script.ScriptService;

import java.util.Map;
import java.util.Set;

public abstract class TestScriptEngine implements ScriptEngine {
public static <F> ScriptService scriptService(ScriptContext<F> context, F factory) {
return new ScriptService(Settings.EMPTY, Map.of("test", new TestScriptEngine() {
@Override
protected Object buildScriptFactory(ScriptContext<?> context) {
return factory;
}

@Override
public Set<ScriptContext<?>> getSupportedContexts() {
return Set.of(context);
}
}), Map.of(context.name, context));
}

@Override
public final String getType() {
return "test";
}

@Override
public final <FactoryType> FactoryType compile(
String name,
String code,
ScriptContext<FactoryType> context,
Map<String, String> params
) {
@SuppressWarnings("unchecked")
FactoryType castFactory = (FactoryType) buildScriptFactory(context);
return castFactory;
}

protected abstract Object buildScriptFactory(ScriptContext<?> context);
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,13 @@
import org.elasticsearch.test.InternalSettingsPlugin;
import org.elasticsearch.xpack.runtimefields.BooleanScriptFieldScript;
import org.elasticsearch.xpack.runtimefields.DateScriptFieldScript;
import org.elasticsearch.xpack.runtimefields.DateScriptFieldScriptTests;
import org.elasticsearch.xpack.runtimefields.DoubleScriptFieldScript;
import org.elasticsearch.xpack.runtimefields.IpScriptFieldScript;
import org.elasticsearch.xpack.runtimefields.LongScriptFieldScript;
import org.elasticsearch.xpack.runtimefields.RuntimeFields;
import org.elasticsearch.xpack.runtimefields.StringScriptFieldScript;
import org.elasticsearch.xpack.runtimefields.TestScriptEngine;

import java.io.IOException;
import java.util.Arrays;
Expand Down Expand Up @@ -314,28 +316,9 @@ private XContentBuilder mapping(String type, CheckedConsumer<XContentBuilder, IO
public static class TestScriptPlugin extends Plugin implements ScriptPlugin {
@Override
public ScriptEngine getScriptEngine(Settings settings, Collection<ScriptContext<?>> contexts) {
return new ScriptEngine() {
return new TestScriptEngine() {
@Override
public String getType() {
return "test";
}

@Override
public <FactoryType> FactoryType compile(
String name,
String code,
ScriptContext<FactoryType> context,
Map<String, String> paramsMap
) {
if ("dummy_source".equals(code)) {
@SuppressWarnings("unchecked")
FactoryType castFactory = (FactoryType) dummyScriptFactory(context);
return castFactory;
}
throw new IllegalArgumentException("No test script for [" + code + "]");
}

private Object dummyScriptFactory(ScriptContext<?> context) {
protected Object buildScriptFactory(ScriptContext<?> context) {
if (context == BooleanScriptFieldScript.CONTEXT) {
return (BooleanScriptFieldScript.Factory) (params, lookup) -> ctx -> new BooleanScriptFieldScript(
params,
Expand All @@ -349,17 +332,7 @@ public void execute() {
};
}
if (context == DateScriptFieldScript.CONTEXT) {
return (DateScriptFieldScript.Factory) (params, lookup, formatter) -> ctx -> new DateScriptFieldScript(
params,
lookup,
formatter,
ctx
) {
@Override
public void execute() {
new DateScriptFieldScript.Millis(this).millis(1595431354874L);
}
};
return DateScriptFieldScriptTests.DUMMY;
}
if (context == DoubleScriptFieldScript.CONTEXT) {
return (DoubleScriptFieldScript.Factory) (params, lookup) -> ctx -> new DoubleScriptFieldScript(
Expand Down

0 comments on commit 40e3aec

Please sign in to comment.