Skip to content

Commit 86727a8

Browse files
authored
Add size_in_bytes to enrich cache stats (#110578)
As preparation for #106081, this PR adds the `size_in_bytes` field to the enrich cache. This field is calculated by summing the ByteReference sizes of all the search hits in the cache. It's not a perfect representation of the size of the enrich cache on the heap, but some experimentation showed that it's quite close.
1 parent be38b0c commit 86727a8

File tree

11 files changed

+117
-18
lines changed

11 files changed

+117
-18
lines changed

docs/changelog/110578.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 110578
2+
summary: Add `size_in_bytes` to enrich cache stats
3+
area: Ingest Node
4+
type: enhancement
5+
issues: []

docs/reference/ingest/apis/enrich/enrich-stats.asciidoc

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,10 @@ The amount of time in milliseconds spent fetching data from the cache on success
121121
`misses_time_in_millis`::
122122
(Long)
123123
The amount of time in milliseconds spent fetching data from the enrich index and updating the cache, on cache misses only.
124+
125+
`size_in_bytes`::
126+
(Long)
127+
An _approximation_ of the size in bytes that the enrich cache takes up on the heap.
124128
--
125129

126130
[[enrich-stats-api-example]]
@@ -172,7 +176,8 @@ The API returns the following response:
172176
"misses": 0,
173177
"evictions": 0,
174178
"hits_time_in_millis": 0,
175-
"misses_time_in_millis": 0
179+
"misses_time_in_millis": 0,
180+
"size_in_bytes": 0
176181
}
177182
]
178183
}
@@ -187,3 +192,4 @@ The API returns the following response:
187192
// TESTRESPONSE[s/"evictions": 0/"evictions" : $body.cache_stats.0.evictions/]
188193
// TESTRESPONSE[s/"hits_time_in_millis": 0/"hits_time_in_millis" : $body.cache_stats.0.hits_time_in_millis/]
189194
// TESTRESPONSE[s/"misses_time_in_millis": 0/"misses_time_in_millis" : $body.cache_stats.0.misses_time_in_millis/]
195+
// TESTRESPONSE[s/"size_in_bytes": 0/"size_in_bytes" : $body.cache_stats.0.size_in_bytes/]

server/src/main/java/org/elasticsearch/TransportVersions.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,7 @@ static TransportVersion def(int id) {
213213
public static final TransportVersion INFERENCE_ADAPTIVE_ALLOCATIONS = def(8_704_00_0);
214214
public static final TransportVersion INDEX_REQUEST_UPDATE_BY_SCRIPT_ORIGIN = def(8_705_00_0);
215215
public static final TransportVersion ML_INFERENCE_COHERE_UNUSED_RERANK_SETTINGS_REMOVED = def(8_706_00_0);
216+
public static final TransportVersion ENRICH_CACHE_STATS_SIZE_ADDED = def(8_707_00_0);
216217

217218
/*
218219
* STOP! READ THIS FIRST! No, really,

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/enrich/action/EnrichStatsAction.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import org.elasticsearch.common.io.stream.StreamInput;
1515
import org.elasticsearch.common.io.stream.StreamOutput;
1616
import org.elasticsearch.common.io.stream.Writeable;
17+
import org.elasticsearch.common.unit.ByteSizeValue;
1718
import org.elasticsearch.core.TimeValue;
1819
import org.elasticsearch.tasks.TaskInfo;
1920
import org.elasticsearch.xcontent.ToXContentFragment;
@@ -195,7 +196,8 @@ public record CacheStats(
195196
long misses,
196197
long evictions,
197198
long hitsTimeInMillis,
198-
long missesTimeInMillis
199+
long missesTimeInMillis,
200+
long cacheSizeInBytes
199201
) implements Writeable, ToXContentFragment {
200202

201203
public CacheStats(StreamInput in) throws IOException {
@@ -206,7 +208,8 @@ public CacheStats(StreamInput in) throws IOException {
206208
in.readVLong(),
207209
in.readVLong(),
208210
in.getTransportVersion().onOrAfter(TransportVersions.ENRICH_CACHE_ADDITIONAL_STATS) ? in.readLong() : -1,
209-
in.getTransportVersion().onOrAfter(TransportVersions.ENRICH_CACHE_ADDITIONAL_STATS) ? in.readLong() : -1
211+
in.getTransportVersion().onOrAfter(TransportVersions.ENRICH_CACHE_ADDITIONAL_STATS) ? in.readLong() : -1,
212+
in.getTransportVersion().onOrAfter(TransportVersions.ENRICH_CACHE_STATS_SIZE_ADDED) ? in.readLong() : -1
210213
);
211214
}
212215

@@ -219,6 +222,7 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
219222
builder.field("evictions", evictions);
220223
builder.humanReadableField("hits_time_in_millis", "hits_time", new TimeValue(hitsTimeInMillis));
221224
builder.humanReadableField("misses_time_in_millis", "misses_time", new TimeValue(missesTimeInMillis));
225+
builder.humanReadableField("size_in_bytes", "size", ByteSizeValue.ofBytes(cacheSizeInBytes));
222226
return builder;
223227
}
224228

@@ -233,6 +237,9 @@ public void writeTo(StreamOutput out) throws IOException {
233237
out.writeLong(hitsTimeInMillis);
234238
out.writeLong(missesTimeInMillis);
235239
}
240+
if (out.getTransportVersion().onOrAfter(TransportVersions.ENRICH_CACHE_STATS_SIZE_ADDED)) {
241+
out.writeLong(cacheSizeInBytes);
242+
}
236243
}
237244
}
238245
}

x-pack/plugin/enrich/qa/rest/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import org.elasticsearch.gradle.internal.info.BuildParams
88

99
restResources {
1010
restApi {
11-
include '_common', 'bulk', 'indices', 'index', 'ingest.delete_pipeline', 'ingest.put_pipeline', 'enrich', 'get'
11+
include '_common', 'bulk', 'indices', 'index', 'ingest.delete_pipeline', 'ingest.put_pipeline', 'enrich', 'get', 'capabilities'
1212
}
1313
restTests {
1414
includeXpack 'enrich'

x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/EnrichCache.java

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,11 @@
5050
*/
5151
public final class EnrichCache {
5252

53-
private final Cache<CacheKey, List<Map<?, ?>>> cache;
53+
private final Cache<CacheKey, CacheValue> cache;
5454
private final LongSupplier relativeNanoTimeProvider;
5555
private final AtomicLong hitsTimeInNanos = new AtomicLong(0);
5656
private final AtomicLong missesTimeInNanos = new AtomicLong(0);
57+
private final AtomicLong sizeInBytes = new AtomicLong(0);
5758
private volatile Metadata metadata;
5859

5960
EnrichCache(long maxSize) {
@@ -63,7 +64,9 @@ public final class EnrichCache {
6364
// non-private for unit testing only
6465
EnrichCache(long maxSize, LongSupplier relativeNanoTimeProvider) {
6566
this.relativeNanoTimeProvider = relativeNanoTimeProvider;
66-
this.cache = CacheBuilder.<CacheKey, List<Map<?, ?>>>builder().setMaximumWeight(maxSize).build();
67+
this.cache = CacheBuilder.<CacheKey, CacheValue>builder().setMaximumWeight(maxSize).removalListener(notification -> {
68+
sizeInBytes.getAndAdd(-1 * notification.getValue().sizeInBytes);
69+
}).build();
6770
}
6871

6972
/**
@@ -86,12 +89,11 @@ public void computeIfAbsent(
8689
hitsTimeInNanos.addAndGet(cacheRequestTime);
8790
listener.onResponse(response);
8891
} else {
89-
9092
final long retrieveStart = relativeNanoTimeProvider.getAsLong();
9193
searchResponseFetcher.accept(searchRequest, ActionListener.wrap(resp -> {
92-
List<Map<?, ?>> value = toCacheValue(resp);
94+
CacheValue value = toCacheValue(resp);
9395
put(searchRequest, value);
94-
List<Map<?, ?>> copy = deepCopy(value, false);
96+
List<Map<?, ?>> copy = deepCopy(value.hits, false);
9597
long databaseQueryAndCachePutTime = relativeNanoTimeProvider.getAsLong() - retrieveStart;
9698
missesTimeInNanos.addAndGet(cacheRequestTime + databaseQueryAndCachePutTime);
9799
listener.onResponse(copy);
@@ -104,20 +106,21 @@ public void computeIfAbsent(
104106
String enrichIndex = getEnrichIndexKey(searchRequest);
105107
CacheKey cacheKey = new CacheKey(enrichIndex, searchRequest);
106108

107-
List<Map<?, ?>> response = cache.get(cacheKey);
109+
CacheValue response = cache.get(cacheKey);
108110
if (response != null) {
109-
return deepCopy(response, false);
111+
return deepCopy(response.hits, false);
110112
} else {
111113
return null;
112114
}
113115
}
114116

115117
// non-private for unit testing only
116-
void put(SearchRequest searchRequest, List<Map<?, ?>> response) {
118+
void put(SearchRequest searchRequest, CacheValue cacheValue) {
117119
String enrichIndex = getEnrichIndexKey(searchRequest);
118120
CacheKey cacheKey = new CacheKey(enrichIndex, searchRequest);
119121

120-
cache.put(cacheKey, response);
122+
cache.put(cacheKey, cacheValue);
123+
sizeInBytes.addAndGet(cacheValue.sizeInBytes);
121124
}
122125

123126
void setMetadata(Metadata metadata) {
@@ -133,7 +136,8 @@ public EnrichStatsAction.Response.CacheStats getStats(String localNodeId) {
133136
cacheStats.getMisses(),
134137
cacheStats.getEvictions(),
135138
TimeValue.nsecToMSec(hitsTimeInNanos.get()),
136-
TimeValue.nsecToMSec(missesTimeInNanos.get())
139+
TimeValue.nsecToMSec(missesTimeInNanos.get()),
140+
sizeInBytes.get()
137141
);
138142
}
139143

@@ -146,12 +150,14 @@ private String getEnrichIndexKey(SearchRequest searchRequest) {
146150
return ia.getIndices().get(0).getName();
147151
}
148152

149-
static List<Map<?, ?>> toCacheValue(SearchResponse response) {
153+
static CacheValue toCacheValue(SearchResponse response) {
150154
List<Map<?, ?>> result = new ArrayList<>(response.getHits().getHits().length);
155+
long size = 0;
151156
for (SearchHit hit : response.getHits()) {
152157
result.add(deepCopy(hit.getSourceAsMap(), true));
158+
size += hit.getSourceRef() != null ? hit.getSourceRef().ramBytesUsed() : 0;
153159
}
154-
return Collections.unmodifiableList(result);
160+
return new CacheValue(Collections.unmodifiableList(result), size);
155161
}
156162

157163
@SuppressWarnings("unchecked")
@@ -205,4 +211,6 @@ public int hashCode() {
205211
}
206212
}
207213

214+
// Visibility for testing
215+
record CacheValue(List<Map<?, ?>> hits, Long sizeInBytes) {}
208216
}

x-pack/plugin/enrich/src/main/java/org/elasticsearch/xpack/enrich/rest/RestEnrichStatsAction.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,15 @@
1616
import org.elasticsearch.xpack.core.enrich.action.EnrichStatsAction;
1717

1818
import java.util.List;
19+
import java.util.Set;
1920

2021
import static org.elasticsearch.rest.RestRequest.Method.GET;
2122

2223
@ServerlessScope(Scope.INTERNAL)
2324
public class RestEnrichStatsAction extends BaseRestHandler {
2425

26+
private static final Set<String> SUPPORTED_CAPABILITIES = Set.of("size-in-bytes");
27+
2528
@Override
2629
public List<Route> routes() {
2730
return List.of(new Route(GET, "/_enrich/_stats"));
@@ -32,6 +35,11 @@ public String getName() {
3235
return "enrich_stats";
3336
}
3437

38+
@Override
39+
public Set<String> supportedCapabilities() {
40+
return SUPPORTED_CAPABILITIES;
41+
}
42+
3543
@Override
3644
protected RestChannelConsumer prepareRequest(final RestRequest restRequest, final NodeClient client) {
3745
final var request = new EnrichStatsAction.Request(RestUtils.getMasterNodeTimeout(restRequest));

x-pack/plugin/enrich/src/test/java/org/elasticsearch/xpack/enrich/EnrichCacheTests.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ public void testCaching() {
7979
new SearchSourceBuilder().query(new MatchQueryBuilder("match_field", "2"))
8080
);
8181
// Emulated search response (content doesn't matter, since it isn't used, it just a cache entry)
82-
List<Map<?, ?>> searchResponse = List.of(Map.of("test", "entry"));
82+
EnrichCache.CacheValue searchResponse = new EnrichCache.CacheValue(List.of(Map.of("test", "entry")), 1L);
8383

8484
EnrichCache enrichCache = new EnrichCache(3);
8585
enrichCache.setMetadata(metadata);
@@ -91,6 +91,7 @@ public void testCaching() {
9191
assertThat(cacheStats.hits(), equalTo(0L));
9292
assertThat(cacheStats.misses(), equalTo(0L));
9393
assertThat(cacheStats.evictions(), equalTo(0L));
94+
assertThat(cacheStats.cacheSizeInBytes(), equalTo(3L));
9495

9596
assertThat(enrichCache.get(searchRequest1), notNullValue());
9697
assertThat(enrichCache.get(searchRequest2), notNullValue());
@@ -101,13 +102,15 @@ public void testCaching() {
101102
assertThat(cacheStats.hits(), equalTo(3L));
102103
assertThat(cacheStats.misses(), equalTo(1L));
103104
assertThat(cacheStats.evictions(), equalTo(0L));
105+
assertThat(cacheStats.cacheSizeInBytes(), equalTo(3L));
104106

105107
enrichCache.put(searchRequest4, searchResponse);
106108
cacheStats = enrichCache.getStats("_id");
107109
assertThat(cacheStats.count(), equalTo(3L));
108110
assertThat(cacheStats.hits(), equalTo(3L));
109111
assertThat(cacheStats.misses(), equalTo(1L));
110112
assertThat(cacheStats.evictions(), equalTo(1L));
113+
assertThat(cacheStats.cacheSizeInBytes(), equalTo(3L));
111114

112115
// Simulate enrich policy execution, which should make current cache entries unused.
113116
metadata = Metadata.builder()
@@ -149,6 +152,7 @@ public void testCaching() {
149152
assertThat(cacheStats.hits(), equalTo(6L));
150153
assertThat(cacheStats.misses(), equalTo(6L));
151154
assertThat(cacheStats.evictions(), equalTo(4L));
155+
assertThat(cacheStats.cacheSizeInBytes(), equalTo(3L));
152156
}
153157

154158
public void testComputeIfAbsent() throws InterruptedException {
@@ -331,7 +335,7 @@ public void testEnrichIndexNotExist() {
331335
new SearchSourceBuilder().query(new MatchQueryBuilder("test", "query"))
332336
);
333337
// Emulated search response (content doesn't matter, since it isn't used, it just a cache entry)
334-
List<Map<?, ?>> searchResponse = List.of(Map.of("test", "entry"));
338+
EnrichCache.CacheValue searchResponse = new EnrichCache.CacheValue(List.of(Map.of("test", "entry")), 1L);
335339

336340
EnrichCache enrichCache = new EnrichCache(1);
337341
enrichCache.setMetadata(metadata);

x-pack/plugin/enrich/src/test/java/org/elasticsearch/xpack/enrich/action/EnrichStatsResponseTests.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ protected EnrichStatsAction.Response createTestInstance() {
5151
randomNonNegativeLong(),
5252
randomNonNegativeLong(),
5353
randomNonNegativeLong(),
54+
randomNonNegativeLong(),
5455
randomNonNegativeLong()
5556
)
5657
);

x-pack/plugin/enrich/src/test/java/org/elasticsearch/xpack/monitoring/collector/enrich/EnrichStatsCollectorTests.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ public void testDoCollect() throws Exception {
9393
randomNonNegativeLong(),
9494
randomNonNegativeLong(),
9595
randomNonNegativeLong(),
96+
randomNonNegativeLong(),
9697
randomNonNegativeLong()
9798
)
9899
);

0 commit comments

Comments
 (0)