Skip to content

Commit d0fa128

Browse files
Add field type cache stats (#193) (#200)
(cherry picked from commit 47dc89e) Signed-off-by: David Zane <davizane@amazon.com> Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent b163c36 commit d0fa128

File tree

9 files changed

+209
-15
lines changed

9 files changed

+209
-15
lines changed

src/main/java/org/opensearch/plugin/insights/core/listener/QueryInsightsListener.java

+1
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ public QueryInsightsListener(
8787
this.clusterService = clusterService;
8888
this.queryInsightsService = queryInsightsService;
8989
this.queryShapeGenerator = new QueryShapeGenerator(clusterService);
90+
queryInsightsService.setQueryShapeGenerator(queryShapeGenerator);
9091

9192
// Setting endpoints set up for top n queries, including enabling top n queries, window size, and top n size
9293
// Expected metricTypes are Latency, CPU, and Memory.

src/main/java/org/opensearch/plugin/insights/core/service/QueryInsightsService.java

+22-3
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,12 @@
1515

1616
import java.io.IOException;
1717
import java.util.ArrayList;
18+
import java.util.Collections;
1819
import java.util.Comparator;
1920
import java.util.HashMap;
2021
import java.util.List;
2122
import java.util.Map;
23+
import java.util.Optional;
2224
import java.util.concurrent.LinkedBlockingQueue;
2325
import java.util.concurrent.TimeUnit;
2426
import java.util.stream.Collectors;
@@ -36,6 +38,7 @@
3638
import org.opensearch.plugin.insights.core.metrics.OperationalMetric;
3739
import org.opensearch.plugin.insights.core.metrics.OperationalMetricsCounter;
3840
import org.opensearch.plugin.insights.core.reader.QueryInsightsReaderFactory;
41+
import org.opensearch.plugin.insights.core.service.categorizer.QueryShapeGenerator;
3942
import org.opensearch.plugin.insights.core.service.categorizer.SearchQueryCategorizer;
4043
import org.opensearch.plugin.insights.rules.model.GroupingType;
4144
import org.opensearch.plugin.insights.rules.model.MetricType;
@@ -100,9 +103,14 @@ public class QueryInsightsService extends AbstractLifecycleComponent {
100103

101104
private volatile boolean searchQueryMetricsEnabled;
102105

103-
private SearchQueryCategorizer searchQueryCategorizer;
106+
private final SearchQueryCategorizer searchQueryCategorizer;
104107

105-
private NamedXContentRegistry namedXContentRegistry;
108+
private final NamedXContentRegistry namedXContentRegistry;
109+
110+
/**
111+
* Query shape generator instance
112+
*/
113+
private QueryShapeGenerator queryShapeGenerator;
106114

107115
/**
108116
* Constructor of the QueryInsightsService
@@ -496,10 +504,14 @@ public QueryInsightsHealthStats getHealthStats() {
496504
Map<MetricType, TopQueriesHealthStats> topQueriesHealthStatsMap = topQueriesServices.entrySet()
497505
.stream()
498506
.collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().getHealthStats()));
507+
Map<String, Long> fieldTypeCacheStats = Optional.ofNullable(queryShapeGenerator)
508+
.map(QueryShapeGenerator::getFieldTypeCacheStats)
509+
.orElse(Collections.emptyMap());
499510
return new QueryInsightsHealthStats(
500511
threadPool.info(QUERY_INSIGHTS_EXECUTOR),
501512
this.queryRecordsQueue.size(),
502-
topQueriesHealthStatsMap
513+
topQueriesHealthStatsMap,
514+
fieldTypeCacheStats
503515
);
504516
}
505517

@@ -511,4 +523,11 @@ private void deleteExpiredTopNIndices() {
511523
topQueriesServices.get(metricType).deleteExpiredTopNIndices(clusterService.state().metadata().indices());
512524
}
513525
}
526+
527+
/**
528+
* Set query shape generator
529+
*/
530+
public void setQueryShapeGenerator(final QueryShapeGenerator queryShapeGenerator) {
531+
this.queryShapeGenerator = queryShapeGenerator;
532+
}
514533
}

src/main/java/org/opensearch/plugin/insights/core/service/categorizer/IndicesFieldTypeCache.java

+60-4
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,18 @@ public class IndicesFieldTypeCache {
2828

2929
private static final Logger logger = LogManager.getLogger(IndicesFieldTypeCache.class);
3030
private final Cache<Index, IndexFieldMap> cache;
31+
/**
32+
* Count of cache evictions
33+
*/
34+
private final CounterMetric evictionCount;
35+
/**
36+
* Count of items in cache
37+
*/
38+
private final CounterMetric entryCount;
39+
/**
40+
* Weight of cache in bytes
41+
*/
42+
private final CounterMetric weight;
3143

3244
public IndicesFieldTypeCache(Settings settings) {
3345
final long sizeInBytes = QueryCategorizationSettings.SEARCH_QUERY_FIELD_TYPE_CACHE_SIZE_KEY.get(settings).getBytes();
@@ -36,9 +48,12 @@ public IndicesFieldTypeCache(Settings settings) {
3648
cacheBuilder.setMaximumWeight(sizeInBytes).weigher((k, v) -> RamUsageEstimator.sizeOfObject(k) + v.weight());
3749
}
3850
cache = cacheBuilder.build();
51+
evictionCount = new CounterMetric();
52+
entryCount = new CounterMetric();
53+
weight = new CounterMetric();
3954
}
4055

41-
public IndexFieldMap getOrInitialize(Index index) {
56+
IndexFieldMap getOrInitialize(Index index) {
4257
try {
4358
return cache.computeIfAbsent(index, k -> new IndexFieldMap());
4459
} catch (ExecutionException ex) {
@@ -52,16 +67,50 @@ public IndexFieldMap getOrInitialize(Index index) {
5267
}
5368

5469
public void invalidate(Index index) {
70+
IndexFieldMap indexFieldMap = cache.get(index);
71+
evictionCount.inc(indexFieldMap.fieldTypeMap.size());
72+
entryCount.dec(indexFieldMap.fieldTypeMap.size());
73+
weight.dec(indexFieldMap.weight());
5574
cache.invalidate(index);
5675
}
5776

5877
public Iterable<Index> keySet() {
5978
return cache.keys();
6079
}
6180

81+
public void incrementCountAndWeight(String key, String value) {
82+
entryCount.inc();
83+
weight.inc(RamUsageEstimator.sizeOf(key) + RamUsageEstimator.sizeOf(value));
84+
}
85+
86+
/**
87+
* Get eviction count
88+
*/
89+
public Long getEvictionCount() {
90+
return evictionCount.count();
91+
}
92+
93+
/**
94+
* Get entry count
95+
*/
96+
public Long getEntryCount() {
97+
return entryCount.count();
98+
}
99+
100+
/**
101+
* Get cache weight in bytes
102+
*/
103+
public Long getWeight() {
104+
return weight.count();
105+
}
106+
62107
static class IndexFieldMap {
63-
private ConcurrentHashMap<String, String> fieldTypeMap;
64-
private CounterMetric weight;
108+
private final ConcurrentHashMap<String, String> fieldTypeMap;
109+
110+
/**
111+
* Estimated memory consumption of fieldTypeMap in bytes
112+
*/
113+
private final CounterMetric weight;
65114

66115
IndexFieldMap() {
67116
fieldTypeMap = new ConcurrentHashMap<>();
@@ -72,11 +121,18 @@ public String get(String fieldName) {
72121
return fieldTypeMap.get(fieldName);
73122
}
74123

75-
public void putIfAbsent(String key, String value) {
124+
/**
125+
* Stores key, value if absent
126+
*
127+
* @return {@code true} if key was absent, else {@code false}
128+
*/
129+
public boolean putIfAbsent(String key, String value) {
76130
// Increment the weight only if the key value pair added to the Map
77131
if (fieldTypeMap.putIfAbsent(key, value) == null) {
78132
weight.inc(RamUsageEstimator.sizeOf(key) + RamUsageEstimator.sizeOf(value));
133+
return true;
79134
}
135+
return false;
80136
}
81137

82138
public long weight() {

src/main/java/org/opensearch/plugin/insights/core/service/categorizer/QueryShapeGenerator.java

+38-2
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,23 @@ public class QueryShapeGenerator implements ClusterStateListener {
3838
static final String EMPTY_STRING = "";
3939
static final String ONE_SPACE_INDENT = " ";
4040
private final ClusterService clusterService;
41-
private final String NO_FIELD_TYPE_VALUE = "";
4241
private final IndicesFieldTypeCache indicesFieldTypeCache;
42+
private long cacheHitCount;
43+
private long cacheMissCount;
44+
45+
private final String NO_FIELD_TYPE_VALUE = "";
46+
public static final String HIT_COUNT = "hit_count";
47+
public static final String MISS_COUNT = "miss_count";
48+
public static final String EVICTIONS = "evictions";
49+
public static final String ENTRY_COUNT = "entry_count";
50+
public static final String SIZE_IN_BYTES = "size_in_bytes";
4351

4452
public QueryShapeGenerator(ClusterService clusterService) {
4553
this.clusterService = clusterService;
4654
clusterService.addListener(this);
4755
this.indicesFieldTypeCache = new IndicesFieldTypeCache(clusterService.getSettings());
56+
this.cacheHitCount = 0;
57+
this.cacheMissCount = 0;
4858
}
4959

5060
public void clusterChanged(ClusterChangedEvent event) {
@@ -369,14 +379,20 @@ String getFieldType(String fieldName, Map<String, Object> propertiesAsMap, Index
369379
String fieldType = getFieldTypeFromCache(fieldName, index);
370380

371381
if (fieldType != null) {
382+
cacheHitCount += 1;
372383
return fieldType;
384+
} else {
385+
cacheMissCount += 1;
373386
}
374387

375388
// Retrieve field type from mapping and cache it if found
376389
fieldType = getFieldTypeFromProperties(fieldName, propertiesAsMap);
377390

378391
// Cache field type or NO_FIELD_TYPE_VALUE if not found
379-
indicesFieldTypeCache.getOrInitialize(index).putIfAbsent(fieldName, fieldType != null ? fieldType : NO_FIELD_TYPE_VALUE);
392+
fieldType = fieldType != null ? fieldType : NO_FIELD_TYPE_VALUE;
393+
if (indicesFieldTypeCache.getOrInitialize(index).putIfAbsent(fieldName, fieldType)) {
394+
indicesFieldTypeCache.incrementCountAndWeight(fieldName, fieldType);
395+
}
380396

381397
return fieldType;
382398
}
@@ -420,4 +436,24 @@ else if (currentMap.containsKey("type")) {
420436
String getFieldTypeFromCache(String fieldName, Index index) {
421437
return indicesFieldTypeCache.getOrInitialize(index).get(fieldName);
422438
}
439+
440+
/**
441+
* Get field type cache stats
442+
*
443+
* @return Map containing cache hit count, miss count, and byte stats
444+
*/
445+
public Map<String, Long> getFieldTypeCacheStats() {
446+
return Map.of(
447+
SIZE_IN_BYTES,
448+
indicesFieldTypeCache.getWeight(),
449+
ENTRY_COUNT,
450+
indicesFieldTypeCache.getEntryCount(),
451+
EVICTIONS,
452+
indicesFieldTypeCache.getEvictionCount(),
453+
HIT_COUNT,
454+
cacheHitCount,
455+
MISS_COUNT,
456+
cacheMissCount
457+
);
458+
}
423459
}

src/main/java/org/opensearch/plugin/insights/rules/model/healthStats/QueryInsightsHealthStats.java

+35-1
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,17 @@
88

99
package org.opensearch.plugin.insights.rules.model.healthStats;
1010

11+
import static org.opensearch.plugin.insights.core.service.categorizer.QueryShapeGenerator.ENTRY_COUNT;
12+
import static org.opensearch.plugin.insights.core.service.categorizer.QueryShapeGenerator.EVICTIONS;
13+
import static org.opensearch.plugin.insights.core.service.categorizer.QueryShapeGenerator.HIT_COUNT;
14+
import static org.opensearch.plugin.insights.core.service.categorizer.QueryShapeGenerator.MISS_COUNT;
15+
import static org.opensearch.plugin.insights.core.service.categorizer.QueryShapeGenerator.SIZE_IN_BYTES;
16+
1117
import java.io.IOException;
18+
import java.util.List;
1219
import java.util.Map;
20+
import java.util.Objects;
21+
import org.opensearch.Version;
1322
import org.opensearch.core.common.io.stream.StreamInput;
1423
import org.opensearch.core.common.io.stream.StreamOutput;
1524
import org.opensearch.core.common.io.stream.Writeable;
@@ -26,10 +35,12 @@ public class QueryInsightsHealthStats implements ToXContentFragment, Writeable {
2635
private final ThreadPool.Info threadPoolInfo;
2736
private final int queryRecordsQueueSize;
2837
private final Map<MetricType, TopQueriesHealthStats> topQueriesHealthStats;
38+
private Map<String, Long> fieldTypeCacheStats;
2939

3040
private static final String THREAD_POOL_INFO = "ThreadPoolInfo";
3141
private static final String QUERY_RECORDS_QUEUE_SIZE = "QueryRecordsQueueSize";
3242
private static final String TOP_QUERIES_HEALTH_STATS = "TopQueriesHealthStats";
43+
private static final String FIELD_TYPE_CACHE_STATS = "FieldTypeCacheStats";
3344

3445
/**
3546
* Constructor to read QueryInsightsHealthStats from a StreamInput.
@@ -41,6 +52,9 @@ public QueryInsightsHealthStats(final StreamInput in) throws IOException {
4152
this.threadPoolInfo = new ThreadPool.Info(in);
4253
this.queryRecordsQueueSize = in.readInt();
4354
this.topQueriesHealthStats = in.readMap(MetricType::readFromStream, TopQueriesHealthStats::new);
55+
if (in.getVersion().onOrAfter(Version.V_2_19_0)) {
56+
this.fieldTypeCacheStats = in.readMap(StreamInput::readString, StreamInput::readLong);
57+
}
4458
}
4559

4660
/**
@@ -53,14 +67,16 @@ public QueryInsightsHealthStats(final StreamInput in) throws IOException {
5367
public QueryInsightsHealthStats(
5468
final ThreadPool.Info threadPoolInfo,
5569
final int queryRecordsQueueSize,
56-
final Map<MetricType, TopQueriesHealthStats> topQueriesHealthStats
70+
final Map<MetricType, TopQueriesHealthStats> topQueriesHealthStats,
71+
final Map<String, Long> fieldTypeCacheStats
5772
) {
5873
if (threadPoolInfo == null || topQueriesHealthStats == null) {
5974
throw new IllegalArgumentException("Parameters cannot be null");
6075
}
6176
this.threadPoolInfo = threadPoolInfo;
6277
this.queryRecordsQueueSize = queryRecordsQueueSize;
6378
this.topQueriesHealthStats = topQueriesHealthStats;
79+
this.fieldTypeCacheStats = Objects.requireNonNull(fieldTypeCacheStats, "fieldTypeCacheStats cannot be null");
6480
}
6581

6682
/**
@@ -87,6 +103,12 @@ public XContentBuilder toXContent(final XContentBuilder builder, final Params pa
87103
builder.endObject();
88104
}
89105
builder.endObject();
106+
// Write field type cache stats
107+
builder.startObject(FIELD_TYPE_CACHE_STATS);
108+
for (String key : List.of(SIZE_IN_BYTES, ENTRY_COUNT, EVICTIONS, HIT_COUNT, MISS_COUNT)) {
109+
builder.field(key, fieldTypeCacheStats.getOrDefault(key, 0L));
110+
}
111+
builder.endObject();
90112
return builder;
91113
}
92114

@@ -105,6 +127,9 @@ public void writeTo(final StreamOutput out) throws IOException {
105127
MetricType::writeTo,
106128
(streamOutput, topQueriesHealthStats) -> topQueriesHealthStats.writeTo(out)
107129
);
130+
if (out.getVersion().onOrAfter(Version.V_2_19_0)) {
131+
out.writeMap(fieldTypeCacheStats, StreamOutput::writeString, StreamOutput::writeLong);
132+
}
108133
}
109134

110135
/**
@@ -133,4 +158,13 @@ public int getQueryRecordsQueueSize() {
133158
public Map<MetricType, TopQueriesHealthStats> getTopQueriesHealthStats() {
134159
return topQueriesHealthStats;
135160
}
161+
162+
/**
163+
* Get the field type cache stats.
164+
*
165+
* @return the field type cache stats
166+
*/
167+
public Map<String, Long> getFieldTypeCacheStats() {
168+
return fieldTypeCacheStats;
169+
}
136170
}

0 commit comments

Comments
 (0)