Skip to content

Commit 94e213d

Browse files
authored
Scripting: Per context stats in script in _nodes/stats (#59266)
Updated `_nodes/stats`: * Update `script` in `_node/stats` to include stats per context: ``` "script": { "compilations": 1, "cache_evictions": 0, "compilation_limit_triggered": 0, "contexts":[ { "context": "aggregation_selector", "compilations": 0, "cache_evictions": 0, "compilation_limit_triggered": 0 }, ``` Refs: #50152 Backport: #59625
1 parent f4caadd commit 94e213d

File tree

9 files changed

+196
-44
lines changed

9 files changed

+196
-44
lines changed

server/src/main/java/org/elasticsearch/action/admin/cluster/node/stats/NodeStats.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -117,10 +117,13 @@ public NodeStats(StreamInput in) throws IOException {
117117
} else {
118118
adaptiveSelectionStats = null;
119119
}
120+
scriptCacheStats = null;
120121
if (in.getVersion().onOrAfter(Version.V_7_8_0)) {
121-
scriptCacheStats = in.readOptionalWriteable(ScriptCacheStats::new);
122-
} else {
123-
scriptCacheStats = null;
122+
if (in.getVersion().before(Version.V_7_9_0)) {
123+
scriptCacheStats = in.readOptionalWriteable(ScriptCacheStats::new);
124+
} else if (scriptStats != null) {
125+
scriptCacheStats = scriptStats.toScriptCacheStats();
126+
}
124127
}
125128
}
126129

@@ -271,8 +274,7 @@ public void writeTo(StreamOutput out) throws IOException {
271274
out.writeOptionalWriteable(ingestStats);
272275
if (out.getVersion().onOrAfter(Version.V_6_1_0)) {
273276
out.writeOptionalWriteable(adaptiveSelectionStats);
274-
}
275-
if (out.getVersion().onOrAfter(Version.V_7_8_0)) {
277+
} if (out.getVersion().onOrAfter(Version.V_7_8_0) && out.getVersion().before(Version.V_7_9_0)) {
276278
out.writeOptionalWriteable(scriptCacheStats);
277279
}
278280
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,10 @@ public ScriptStats stats() {
136136
return scriptMetrics.stats();
137137
}
138138

139+
public ScriptContextStats stats(String context) {
140+
return scriptMetrics.stats(context);
141+
}
142+
139143
/**
140144
* Check whether there have been too many compilations within the last minute, throwing a circuit breaking exception if so.
141145
* This is a variant of the token bucket algorithm: https://en.wikipedia.org/wiki/Token_bucket

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

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,9 @@
3232
import java.util.Objects;
3333
import java.util.stream.Collectors;
3434

35+
// This class is deprecated in favor of ScriptStats and ScriptContextStats. It is removed in 8.
3536
public class ScriptCacheStats implements Writeable, ToXContentFragment {
36-
private final Map<String,ScriptStats> context;
37+
private final Map<String, ScriptStats> context;
3738
private final ScriptStats general;
3839

3940
public ScriptCacheStats(Map<String, ScriptStats> context) {
@@ -135,7 +136,19 @@ public ScriptStats sum() {
135136
if (general != null) {
136137
return general;
137138
}
138-
return ScriptStats.sum(context.values());
139+
long compilations = 0;
140+
long cacheEvictions = 0;
141+
long compilationLimitTriggered = 0;
142+
for (ScriptStats stat: context.values()) {
143+
compilations += stat.getCompilations();
144+
cacheEvictions += stat.getCacheEvictions();
145+
compilationLimitTriggered += stat.getCompilationLimitTriggered();
146+
}
147+
return new ScriptStats(
148+
compilations,
149+
cacheEvictions,
150+
compilationLimitTriggered
151+
);
139152
}
140153

141154
static final class Fields {
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.elasticsearch.script;
21+
22+
import org.elasticsearch.common.io.stream.StreamInput;
23+
import org.elasticsearch.common.io.stream.StreamOutput;
24+
import org.elasticsearch.common.io.stream.Writeable;
25+
import org.elasticsearch.common.xcontent.ToXContentFragment;
26+
import org.elasticsearch.common.xcontent.XContentBuilder;
27+
28+
import java.io.IOException;
29+
import java.util.Objects;
30+
31+
public class ScriptContextStats implements Writeable, ToXContentFragment, Comparable<ScriptContextStats> {
32+
private final String context;
33+
private final long compilations;
34+
private final long cacheEvictions;
35+
private final long compilationLimitTriggered;
36+
37+
public ScriptContextStats(String context, long compilations, long cacheEvictions, long compilationLimitTriggered) {
38+
this.context = Objects.requireNonNull(context);
39+
this.compilations = compilations;
40+
this.cacheEvictions = cacheEvictions;
41+
this.compilationLimitTriggered = compilationLimitTriggered;
42+
}
43+
44+
public ScriptContextStats(StreamInput in) throws IOException {
45+
context = in.readString();
46+
compilations = in.readVLong();
47+
cacheEvictions = in.readVLong();
48+
compilationLimitTriggered = in.readVLong();
49+
}
50+
51+
@Override
52+
public void writeTo(StreamOutput out) throws IOException {
53+
out.writeString(context);
54+
out.writeVLong(compilations);
55+
out.writeVLong(cacheEvictions);
56+
out.writeVLong(compilationLimitTriggered);
57+
}
58+
59+
public String getContext() {
60+
return context;
61+
}
62+
63+
public long getCompilations() {
64+
return compilations;
65+
}
66+
67+
public long getCacheEvictions() {
68+
return cacheEvictions;
69+
}
70+
71+
public long getCompilationLimitTriggered() {
72+
return compilationLimitTriggered;
73+
}
74+
75+
@Override
76+
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
77+
builder.startObject();
78+
builder.field(Fields.CONTEXT, getContext());
79+
builder.field(Fields.COMPILATIONS, getCompilations());
80+
builder.field(Fields.CACHE_EVICTIONS, getCacheEvictions());
81+
builder.field(Fields.COMPILATION_LIMIT_TRIGGERED, getCompilationLimitTriggered());
82+
builder.endObject();
83+
return builder;
84+
}
85+
86+
@Override
87+
public int compareTo(ScriptContextStats o) {
88+
return this.context.compareTo(o.context);
89+
}
90+
91+
static final class Fields {
92+
static final String CONTEXT = "context";
93+
static final String COMPILATIONS = "compilations";
94+
static final String CACHE_EVICTIONS = "cache_evictions";
95+
static final String COMPILATION_LIMIT_TRIGGERED = "compilation_limit_triggered";
96+
}
97+
}

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

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,6 @@ public class ScriptMetrics {
2626
final CounterMetric cacheEvictionsMetric = new CounterMetric();
2727
final CounterMetric compilationLimitTriggered = new CounterMetric();
2828

29-
public ScriptStats stats() {
30-
return new ScriptStats(compilationsMetric.count(), cacheEvictionsMetric.count(), compilationLimitTriggered.count());
31-
}
32-
3329
public void onCompilation() {
3430
compilationsMetric.inc();
3531
}
@@ -41,4 +37,17 @@ public void onCacheEviction() {
4137
public void onCompilationLimit() {
4238
compilationLimitTriggered.inc();
4339
}
40+
41+
public ScriptStats stats() {
42+
return new ScriptStats(compilationsMetric.count(), cacheEvictionsMetric.count(), compilationLimitTriggered.count());
43+
}
44+
45+
public ScriptContextStats stats(String context) {
46+
return new ScriptContextStats(
47+
context,
48+
compilationsMetric.count(),
49+
cacheEvictionsMetric.count(),
50+
compilationLimitTriggered.count()
51+
);
52+
}
4453
}

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -658,7 +658,12 @@ ScriptStats stats() {
658658
if (general != null) {
659659
return general.stats();
660660
}
661-
return ScriptStats.sum(contextCache.values().stream().map(AtomicReference::get).map(ScriptCache::stats)::iterator);
661+
List<ScriptContextStats> contextStats = new ArrayList<>(contextCache.size());
662+
for (Map.Entry<String, AtomicReference<ScriptCache>> entry : contextCache.entrySet()) {
663+
ScriptCache cache = entry.getValue().get();
664+
contextStats.add(cache.stats(entry.getKey()));
665+
}
666+
return new ScriptStats(contextStats);
662667
}
663668

664669
ScriptCacheStats cacheStats() {

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

Lines changed: 52 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -27,22 +27,52 @@
2727
import org.elasticsearch.common.xcontent.XContentBuilder;
2828

2929
import java.io.IOException;
30+
import java.util.ArrayList;
31+
import java.util.Collections;
32+
import java.util.HashMap;
33+
import java.util.List;
34+
import java.util.Map;
3035

3136
public class ScriptStats implements Writeable, ToXContentFragment {
37+
private final List<ScriptContextStats> contextStats;
3238
private final long compilations;
3339
private final long cacheEvictions;
3440
private final long compilationLimitTriggered;
3541

42+
public ScriptStats(List<ScriptContextStats> contextStats) {
43+
ArrayList<ScriptContextStats> ctxStats = new ArrayList<>(contextStats.size());
44+
ctxStats.addAll(contextStats);
45+
ctxStats.sort(ScriptContextStats::compareTo);
46+
this.contextStats = Collections.unmodifiableList(ctxStats);
47+
long compilations = 0;
48+
long cacheEvictions = 0;
49+
long compilationLimitTriggered = 0;
50+
for (ScriptContextStats stats: contextStats) {
51+
compilations += stats.getCompilations();
52+
cacheEvictions += stats.getCacheEvictions();
53+
compilationLimitTriggered += stats.getCompilationLimitTriggered();
54+
}
55+
this.compilations = compilations;
56+
this.cacheEvictions = cacheEvictions;
57+
this.compilationLimitTriggered = compilationLimitTriggered;
58+
}
59+
3660
public ScriptStats(long compilations, long cacheEvictions, long compilationLimitTriggered) {
61+
this.contextStats = Collections.emptyList();
3762
this.compilations = compilations;
3863
this.cacheEvictions = cacheEvictions;
3964
this.compilationLimitTriggered = compilationLimitTriggered;
4065
}
4166

67+
public ScriptStats(ScriptContextStats context) {
68+
this(context.getCompilations(), context.getCacheEvictions(), context.getCompilationLimitTriggered());
69+
}
70+
4271
public ScriptStats(StreamInput in) throws IOException {
4372
compilations = in.readVLong();
4473
cacheEvictions = in.readVLong();
4574
compilationLimitTriggered = in.getVersion().onOrAfter(Version.V_7_0_0) ? in.readVLong() : 0;
75+
contextStats = in.getVersion().onOrAfter(Version.V_7_9_0) ? in.readList(ScriptContextStats::new) : Collections.emptyList();
4676
}
4777

4878
@Override
@@ -52,6 +82,13 @@ public void writeTo(StreamOutput out) throws IOException {
5282
if (out.getVersion().onOrAfter(Version.V_7_0_0)) {
5383
out.writeVLong(compilationLimitTriggered);
5484
}
85+
if (out.getVersion().onOrAfter(Version.V_7_9_0)) {
86+
out.writeList(contextStats);
87+
}
88+
}
89+
90+
public List<ScriptContextStats> getContextStats() {
91+
return contextStats;
5592
}
5693

5794
public long getCompilations() {
@@ -66,36 +103,32 @@ public long getCompilationLimitTriggered() {
66103
return compilationLimitTriggered;
67104
}
68105

106+
public ScriptCacheStats toScriptCacheStats() {
107+
if (contextStats.isEmpty()) {
108+
return new ScriptCacheStats(this);
109+
}
110+
Map<String, ScriptStats> contexts = new HashMap<>(contextStats.size());
111+
for (ScriptContextStats contextStats : contextStats) {
112+
contexts.put(contextStats.getContext(), new ScriptStats(contextStats));
113+
}
114+
return new ScriptCacheStats(contexts);
115+
}
116+
69117
@Override
70118
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
71119
builder.startObject(Fields.SCRIPT_STATS);
72-
builder.field(Fields.COMPILATIONS, getCompilations());
73-
builder.field(Fields.CACHE_EVICTIONS, getCacheEvictions());
74-
builder.field(Fields.COMPILATION_LIMIT_TRIGGERED, getCompilationLimitTriggered());
120+
builder.field(Fields.COMPILATIONS, compilations);
121+
builder.field(Fields.CACHE_EVICTIONS, cacheEvictions);
122+
builder.field(Fields.COMPILATION_LIMIT_TRIGGERED, compilationLimitTriggered);
75123
builder.endObject();
76124
return builder;
77125
}
78126

79127
static final class Fields {
80128
static final String SCRIPT_STATS = "script";
129+
static final String CONTEXTS = "contexts";
81130
static final String COMPILATIONS = "compilations";
82131
static final String CACHE_EVICTIONS = "cache_evictions";
83132
static final String COMPILATION_LIMIT_TRIGGERED = "compilation_limit_triggered";
84133
}
85-
86-
public static ScriptStats sum(Iterable<ScriptStats> stats) {
87-
long compilations = 0;
88-
long cacheEvictions = 0;
89-
long compilationLimitTriggered = 0;
90-
for (ScriptStats stat: stats) {
91-
compilations += stat.compilations;
92-
cacheEvictions += stat.cacheEvictions;
93-
compilationLimitTriggered += stat.compilationLimitTriggered;
94-
}
95-
return new ScriptStats(
96-
compilations,
97-
cacheEvictions,
98-
compilationLimitTriggered
99-
);
100-
}
101134
}

server/src/test/java/org/elasticsearch/action/admin/cluster/node/stats/NodeStatsTests.java

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ public void testSerialization() throws IOException {
316316
ScriptCacheStats deserializedScriptCacheStats = deserializedNodeStats.getScriptCacheStats();
317317
if (scriptCacheStats == null) {
318318
assertNull(deserializedScriptCacheStats);
319-
} else {
319+
} else if (deserializedScriptCacheStats.getContextStats() != null) {
320320
Map<String, ScriptStats> deserialized = deserializedScriptCacheStats.getContextStats();
321321
long evictions = 0;
322322
long limited = 0;
@@ -514,16 +514,7 @@ public static NodeStats createNodeStats() {
514514
}
515515
adaptiveSelectionStats = new AdaptiveSelectionStats(nodeConnections, nodeStats);
516516
}
517-
ScriptCacheStats scriptCacheStats = null;
518-
if (frequently()) {
519-
int numContents = randomIntBetween(0, 20);
520-
Map<String,ScriptStats> stats = new HashMap<>(numContents);
521-
for (int i = 0; i < numContents; i++) {
522-
String context = randomValueOtherThanMany(stats::containsKey, () -> randomAlphaOfLength(12));
523-
stats.put(context, new ScriptStats(randomLongBetween(0, 1024), randomLongBetween(0, 1024), randomLongBetween(0, 1024)));
524-
}
525-
scriptCacheStats = new ScriptCacheStats(stats);
526-
}
517+
ScriptCacheStats scriptCacheStats = scriptStats != null ? scriptStats.toScriptCacheStats() : null;
527518
//TODO NodeIndicesStats are not tested here, way too complicated to create, also they need to be migrated to Writeable yet
528519
return new NodeStats(node, randomNonNegativeLong(), null, osStats, processStats, jvmStats, threadPoolStats,
529520
fsInfo, transportStats, httpStats, allCircuitBreakerStats, scriptStats, discoveryStats,

server/src/test/java/org/elasticsearch/script/ScriptServiceTests.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@
3737
import org.junit.Before;
3838

3939
import java.io.IOException;
40-
import java.util.Arrays;
4140
import java.util.Collections;
4241
import java.util.HashMap;
4342
import java.util.Map;
@@ -585,7 +584,6 @@ public void testCacheHolderChangeSettings() throws IOException {
585584
);
586585
assertEquals(contexts.keySet(), scriptService.cacheHolder.get().contextCache.keySet());
587586

588-
String d = randomValueOtherThanMany(Arrays.asList(a, b, c)::contains, () -> randomFrom(contextNames));
589587
assertEquals(new ScriptCache.CompilationRate(aRate),
590588
scriptService.cacheHolder.get().contextCache.get(a).get().rate);
591589
assertEquals(new ScriptCache.CompilationRate(bRate),

0 commit comments

Comments
 (0)