Skip to content

Opt date valued script fields out of rate limit #61238

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've only done this to a single script field. If we like it I'd be happy to do it in a follow up.


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