Skip to content

Commit 40e3aec

Browse files
authored
Opt date valued script fields out of rate limit (#61238)
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.
1 parent b5e8524 commit 40e3aec

File tree

6 files changed

+120
-34
lines changed

6 files changed

+120
-34
lines changed

server/src/main/java/org/elasticsearch/script/ScriptCache.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public class ScriptCache {
4242

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

45-
static final CompilationRate UNLIMITED_COMPILATION_RATE = new CompilationRate(0, TimeValue.ZERO);
45+
public static final CompilationRate UNLIMITED_COMPILATION_RATE = new CompilationRate(0, TimeValue.ZERO);
4646

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

x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/AbstractScriptFieldScript.java

+26
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,42 @@
99
import org.apache.lucene.index.LeafReaderContext;
1010
import org.elasticsearch.index.fielddata.ScriptDocValues;
1111
import org.elasticsearch.script.AggregationScript;
12+
import org.elasticsearch.script.ScriptCache;
13+
import org.elasticsearch.script.ScriptContext;
1214
import org.elasticsearch.search.lookup.LeafSearchLookup;
1315
import org.elasticsearch.search.lookup.SearchLookup;
1416

1517
import java.util.Map;
1618

19+
import static org.elasticsearch.common.unit.TimeValue.timeValueMillis;
20+
1721
/**
1822
* Abstract base for scripts to execute to build scripted fields. Inspired by
1923
* {@link AggregationScript} but hopefully with less historical baggage.
2024
*/
2125
public abstract class AbstractScriptFieldScript {
26+
public static <F> ScriptContext<F> newContext(String name, Class<F> factoryClass) {
27+
return new ScriptContext<F>(
28+
name + "_script_field",
29+
factoryClass,
30+
/*
31+
* In an ideal world we wouldn't need the script cache at all
32+
* because we have a hard reference to the script. The trouble
33+
* is that we compile the scripts a few times when performing
34+
* a mapping update. This is unfortunate, but we rely on the
35+
* cache to speed this up.
36+
*/
37+
100,
38+
timeValueMillis(0),
39+
/*
40+
* Disable compilation rate limits for scripted fields so we
41+
* don't prevent mapping updates because we've performed too
42+
* many recently. That'd just be lame.
43+
*/
44+
ScriptCache.UNLIMITED_COMPILATION_RATE.asTuple()
45+
);
46+
}
47+
2248
private final Map<String, Object> params;
2349
private final LeafSearchLookup leafSearchLookup;
2450

x-pack/plugin/runtime-fields/src/main/java/org/elasticsearch/xpack/runtimefields/DateScriptFieldScript.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
import java.util.Map;
2222

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

2626
static List<Whitelist> whitelist() {
2727
return List.of(WhitelistLoader.loadFromResourceFiles(RuntimeFieldsPainlessExtension.class, "date_whitelist.txt"));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
package org.elasticsearch.xpack.runtimefields;
8+
9+
import org.elasticsearch.script.Script;
10+
import org.elasticsearch.script.ScriptService;
11+
import org.elasticsearch.script.ScriptType;
12+
import org.elasticsearch.test.ESTestCase;
13+
14+
import java.io.IOException;
15+
import java.util.Map;
16+
17+
public class DateScriptFieldScriptTests extends ESTestCase {
18+
public static final DateScriptFieldScript.Factory DUMMY = (params, lookup, formatter) -> ctx -> new DateScriptFieldScript(
19+
params,
20+
lookup,
21+
formatter,
22+
ctx
23+
) {
24+
@Override
25+
public void execute() {
26+
new DateScriptFieldScript.Millis(this).millis(1595431354874L);
27+
}
28+
};
29+
30+
public void testRateLimitingDisabled() throws IOException {
31+
try (ScriptService scriptService = TestScriptEngine.scriptService(DateScriptFieldScript.CONTEXT, DUMMY)) {
32+
for (int i = 0; i < 1000; i++) {
33+
scriptService.compile(new Script(ScriptType.INLINE, "test", "test_" + i, Map.of()), DateScriptFieldScript.CONTEXT);
34+
}
35+
}
36+
}
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
package org.elasticsearch.xpack.runtimefields;
8+
9+
import org.elasticsearch.common.settings.Settings;
10+
import org.elasticsearch.script.ScriptContext;
11+
import org.elasticsearch.script.ScriptEngine;
12+
import org.elasticsearch.script.ScriptService;
13+
14+
import java.util.Map;
15+
import java.util.Set;
16+
17+
public abstract class TestScriptEngine implements ScriptEngine {
18+
public static <F> ScriptService scriptService(ScriptContext<F> context, F factory) {
19+
return new ScriptService(Settings.EMPTY, Map.of("test", new TestScriptEngine() {
20+
@Override
21+
protected Object buildScriptFactory(ScriptContext<?> context) {
22+
return factory;
23+
}
24+
25+
@Override
26+
public Set<ScriptContext<?>> getSupportedContexts() {
27+
return Set.of(context);
28+
}
29+
}), Map.of(context.name, context));
30+
}
31+
32+
@Override
33+
public final String getType() {
34+
return "test";
35+
}
36+
37+
@Override
38+
public final <FactoryType> FactoryType compile(
39+
String name,
40+
String code,
41+
ScriptContext<FactoryType> context,
42+
Map<String, String> params
43+
) {
44+
@SuppressWarnings("unchecked")
45+
FactoryType castFactory = (FactoryType) buildScriptFactory(context);
46+
return castFactory;
47+
}
48+
49+
protected abstract Object buildScriptFactory(ScriptContext<?> context);
50+
}

x-pack/plugin/runtime-fields/src/test/java/org/elasticsearch/xpack/runtimefields/mapper/RuntimeScriptFieldMapperTests.java

+5-32
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,13 @@
2525
import org.elasticsearch.test.InternalSettingsPlugin;
2626
import org.elasticsearch.xpack.runtimefields.BooleanScriptFieldScript;
2727
import org.elasticsearch.xpack.runtimefields.DateScriptFieldScript;
28+
import org.elasticsearch.xpack.runtimefields.DateScriptFieldScriptTests;
2829
import org.elasticsearch.xpack.runtimefields.DoubleScriptFieldScript;
2930
import org.elasticsearch.xpack.runtimefields.IpScriptFieldScript;
3031
import org.elasticsearch.xpack.runtimefields.LongScriptFieldScript;
3132
import org.elasticsearch.xpack.runtimefields.RuntimeFields;
3233
import org.elasticsearch.xpack.runtimefields.StringScriptFieldScript;
34+
import org.elasticsearch.xpack.runtimefields.TestScriptEngine;
3335

3436
import java.io.IOException;
3537
import java.util.Arrays;
@@ -314,28 +316,9 @@ private XContentBuilder mapping(String type, CheckedConsumer<XContentBuilder, IO
314316
public static class TestScriptPlugin extends Plugin implements ScriptPlugin {
315317
@Override
316318
public ScriptEngine getScriptEngine(Settings settings, Collection<ScriptContext<?>> contexts) {
317-
return new ScriptEngine() {
319+
return new TestScriptEngine() {
318320
@Override
319-
public String getType() {
320-
return "test";
321-
}
322-
323-
@Override
324-
public <FactoryType> FactoryType compile(
325-
String name,
326-
String code,
327-
ScriptContext<FactoryType> context,
328-
Map<String, String> paramsMap
329-
) {
330-
if ("dummy_source".equals(code)) {
331-
@SuppressWarnings("unchecked")
332-
FactoryType castFactory = (FactoryType) dummyScriptFactory(context);
333-
return castFactory;
334-
}
335-
throw new IllegalArgumentException("No test script for [" + code + "]");
336-
}
337-
338-
private Object dummyScriptFactory(ScriptContext<?> context) {
321+
protected Object buildScriptFactory(ScriptContext<?> context) {
339322
if (context == BooleanScriptFieldScript.CONTEXT) {
340323
return (BooleanScriptFieldScript.Factory) (params, lookup) -> ctx -> new BooleanScriptFieldScript(
341324
params,
@@ -349,17 +332,7 @@ public void execute() {
349332
};
350333
}
351334
if (context == DateScriptFieldScript.CONTEXT) {
352-
return (DateScriptFieldScript.Factory) (params, lookup, formatter) -> ctx -> new DateScriptFieldScript(
353-
params,
354-
lookup,
355-
formatter,
356-
ctx
357-
) {
358-
@Override
359-
public void execute() {
360-
new DateScriptFieldScript.Millis(this).millis(1595431354874L);
361-
}
362-
};
335+
return DateScriptFieldScriptTests.DUMMY;
363336
}
364337
if (context == DoubleScriptFieldScript.CONTEXT) {
365338
return (DoubleScriptFieldScript.Factory) (params, lookup) -> ctx -> new DoubleScriptFieldScript(

0 commit comments

Comments
 (0)