Watcher: Reduce script cache churn by checking for mustache tags (#33978)
Watcher is using a lot of so called TextTemplate fields in a watch definition, which can use mustache to insert the watch id for example. For the user it is non-obvious which field is just a string field or which field is a text template. This also means, that for every such field, we currently do a script compilation, even if the field does not contain any mustache syntax. This will lead to an increased script cache churn, because those compiled scripts (that only contain a string), will evict other scripts. On top of that, this also means that an unneeded compilation has happened, instead of returning that string immediately. The usages of mustache templating are in all of the actions (most of the time far more than one compilation) as well as most of the inputs. Especially when running a lot of watches in parallel, this will reduce execution times and help reuse of real scripts.
This commit is contained in:
parent
bda7bc145b
commit
a15b1b97d2
|
@ -35,6 +35,11 @@ public class TextTemplateEngine extends AbstractComponent {
|
||||||
String mediaType = compileParams(detectContentType(template));
|
String mediaType = compileParams(detectContentType(template));
|
||||||
template = trimContentType(textTemplate);
|
template = trimContentType(textTemplate);
|
||||||
|
|
||||||
|
int indexStartMustacheExpression = template.indexOf("{{");
|
||||||
|
if (indexStartMustacheExpression == -1) {
|
||||||
|
return template;
|
||||||
|
}
|
||||||
|
|
||||||
Map<String, Object> mergedModel = new HashMap<>();
|
Map<String, Object> mergedModel = new HashMap<>();
|
||||||
if (textTemplate.getParams() != null) {
|
if (textTemplate.getParams() != null) {
|
||||||
mergedModel.putAll(textTemplate.getParams());
|
mergedModel.putAll(textTemplate.getParams());
|
||||||
|
|
|
@ -21,6 +21,7 @@ import org.junit.Before;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static java.util.Collections.singletonMap;
|
import static java.util.Collections.singletonMap;
|
||||||
|
@ -31,7 +32,10 @@ import static org.hamcrest.Matchers.equalTo;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
import static org.hamcrest.Matchers.notNullValue;
|
import static org.hamcrest.Matchers.notNullValue;
|
||||||
import static org.hamcrest.Matchers.nullValue;
|
import static org.hamcrest.Matchers.nullValue;
|
||||||
|
import static org.mockito.Matchers.any;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
public class TextTemplateTests extends ESTestCase {
|
public class TextTemplateTests extends ESTestCase {
|
||||||
|
@ -47,7 +51,7 @@ public class TextTemplateTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testRender() throws Exception {
|
public void testRender() throws Exception {
|
||||||
String templateText = "_template";
|
String templateText = "{{_template}}";
|
||||||
Map<String, Object> params = singletonMap("param_key", "param_val");
|
Map<String, Object> params = singletonMap("param_key", "param_val");
|
||||||
Map<String, Object> model = singletonMap("model_key", "model_val");
|
Map<String, Object> model = singletonMap("model_key", "model_val");
|
||||||
Map<String, Object> merged = new HashMap<>(params);
|
Map<String, Object> merged = new HashMap<>(params);
|
||||||
|
@ -72,7 +76,7 @@ public class TextTemplateTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testRenderOverridingModel() throws Exception {
|
public void testRenderOverridingModel() throws Exception {
|
||||||
String templateText = "_template";
|
String templateText = "{{_template}}";
|
||||||
Map<String, Object> params = singletonMap("key", "param_val");
|
Map<String, Object> params = singletonMap("key", "param_val");
|
||||||
Map<String, Object> model = singletonMap("key", "model_val");
|
Map<String, Object> model = singletonMap("key", "model_val");
|
||||||
ScriptType type = randomFrom(ScriptType.values());
|
ScriptType type = randomFrom(ScriptType.values());
|
||||||
|
@ -94,7 +98,7 @@ public class TextTemplateTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testRenderDefaults() throws Exception {
|
public void testRenderDefaults() throws Exception {
|
||||||
String templateText = "_template";
|
String templateText = "{{_template}}";
|
||||||
Map<String, Object> model = singletonMap("key", "model_val");
|
Map<String, Object> model = singletonMap("key", "model_val");
|
||||||
|
|
||||||
TemplateScript.Factory compiledTemplate = templateParams ->
|
TemplateScript.Factory compiledTemplate = templateParams ->
|
||||||
|
@ -113,6 +117,39 @@ public class TextTemplateTests extends ESTestCase {
|
||||||
assertThat(engine.render(template, model), is("rendered_text"));
|
assertThat(engine.render(template, model), is("rendered_text"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testDontInvokeScriptServiceOnNonMustacheText() {
|
||||||
|
assertNoCompilation("this is my text");
|
||||||
|
assertScriptServiceInvoked("}}{{");
|
||||||
|
assertScriptServiceInvoked("}}{{ctx.payload}}");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertNoCompilation(String input) {
|
||||||
|
String output = engine.render(new TextTemplate(input), Collections.emptyMap());
|
||||||
|
assertThat(input, is(output));
|
||||||
|
verifyZeroInteractions(service);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertScriptServiceInvoked(final String input) {
|
||||||
|
ScriptService scriptService = mock(ScriptService.class);
|
||||||
|
TextTemplateEngine e = new TextTemplateEngine(Settings.EMPTY, scriptService);
|
||||||
|
|
||||||
|
TemplateScript.Factory compiledTemplate = templateParams ->
|
||||||
|
new TemplateScript(templateParams) {
|
||||||
|
@Override
|
||||||
|
public String execute() {
|
||||||
|
return input.toUpperCase(Locale.ROOT);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
when(scriptService.compile(new Script(ScriptType.INLINE, lang, input,
|
||||||
|
Collections.singletonMap("content_type", "text/plain"), Collections.emptyMap()), Watcher.SCRIPT_TEMPLATE_CONTEXT))
|
||||||
|
.thenReturn(compiledTemplate);
|
||||||
|
|
||||||
|
String output = e.render(new TextTemplate(input), Collections.emptyMap());
|
||||||
|
verify(scriptService).compile(any(), any());
|
||||||
|
assertThat(output, is(input.toUpperCase(Locale.ROOT)));
|
||||||
|
}
|
||||||
|
|
||||||
public void testParser() throws Exception {
|
public void testParser() throws Exception {
|
||||||
ScriptType type = randomScriptType();
|
ScriptType type = randomScriptType();
|
||||||
TextTemplate template =
|
TextTemplate template =
|
||||||
|
|
Loading…
Reference in New Issue