Skip to content

Commit 1309cbc

Browse files
committed
Add field type cache stats
Signed-off-by: David Zane <davizane@amazon.com>
1 parent 1d991b8 commit 1309cbc

File tree

9 files changed

+144
-12
lines changed

9 files changed

+144
-12
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

+26-3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88

99
package org.opensearch.plugin.insights.core.service;
1010

11+
import static org.opensearch.plugin.insights.core.service.categorizer.QueryShapeGenerator.BYTES_STRING;
12+
import static org.opensearch.plugin.insights.core.service.categorizer.QueryShapeGenerator.HITS_STRING;
13+
import static org.opensearch.plugin.insights.core.service.categorizer.QueryShapeGenerator.MISSES_STRING;
1114
import static org.opensearch.plugin.insights.settings.QueryInsightsSettings.DEFAULT_GROUPING_TYPE;
1215
import static org.opensearch.plugin.insights.settings.QueryInsightsSettings.QUERY_INSIGHTS_EXECUTOR;
1316
import static org.opensearch.plugin.insights.settings.QueryInsightsSettings.TOP_N_EXPORTER_DELETE_AFTER;
@@ -19,6 +22,7 @@
1922
import java.util.HashMap;
2023
import java.util.List;
2124
import java.util.Map;
25+
import java.util.Optional;
2226
import java.util.concurrent.LinkedBlockingQueue;
2327
import java.util.concurrent.TimeUnit;
2428
import java.util.stream.Collectors;
@@ -36,6 +40,7 @@
3640
import org.opensearch.plugin.insights.core.metrics.OperationalMetric;
3741
import org.opensearch.plugin.insights.core.metrics.OperationalMetricsCounter;
3842
import org.opensearch.plugin.insights.core.reader.QueryInsightsReaderFactory;
43+
import org.opensearch.plugin.insights.core.service.categorizer.QueryShapeGenerator;
3944
import org.opensearch.plugin.insights.core.service.categorizer.SearchQueryCategorizer;
4045
import org.opensearch.plugin.insights.rules.model.GroupingType;
4146
import org.opensearch.plugin.insights.rules.model.MetricType;
@@ -100,9 +105,16 @@ public class QueryInsightsService extends AbstractLifecycleComponent {
100105

101106
private volatile boolean searchQueryMetricsEnabled;
102107

103-
private SearchQueryCategorizer searchQueryCategorizer;
108+
private final SearchQueryCategorizer searchQueryCategorizer;
104109

105-
private NamedXContentRegistry namedXContentRegistry;
110+
private final NamedXContentRegistry namedXContentRegistry;
111+
112+
/**
113+
* Query shape generator instance
114+
*/
115+
private QueryShapeGenerator queryShapeGenerator;
116+
117+
private static final Map<String, Long> EMPTY_FIELD_TYPE_CACHE_STATS = Map.of(HITS_STRING, 0L, MISSES_STRING, 0L, BYTES_STRING, 0L);
106118

107119
/**
108120
* Constructor of the QueryInsightsService
@@ -496,10 +508,14 @@ public QueryInsightsHealthStats getHealthStats() {
496508
Map<MetricType, TopQueriesHealthStats> topQueriesHealthStatsMap = topQueriesServices.entrySet()
497509
.stream()
498510
.collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().getHealthStats()));
511+
Map<String, Long> fieldTypeCacheStats = Optional.ofNullable(queryShapeGenerator)
512+
.map(QueryShapeGenerator::getFieldTypeCacheStats)
513+
.orElse(EMPTY_FIELD_TYPE_CACHE_STATS);
499514
return new QueryInsightsHealthStats(
500515
threadPool.info(QUERY_INSIGHTS_EXECUTOR),
501516
this.queryRecordsQueue.size(),
502-
topQueriesHealthStatsMap
517+
topQueriesHealthStatsMap,
518+
fieldTypeCacheStats
503519
);
504520
}
505521

@@ -511,4 +527,11 @@ private void deleteExpiredTopNIndices() {
511527
topQueriesServices.get(metricType).deleteExpiredTopNIndices(clusterService.state().metadata().indices());
512528
}
513529
}
530+
531+
/**
532+
* Set query shape generator
533+
*/
534+
public void setQueryShapeGenerator(final QueryShapeGenerator queryShapeGenerator) {
535+
this.queryShapeGenerator = queryShapeGenerator;
536+
}
514537
}

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

+27-3
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public IndicesFieldTypeCache(Settings settings) {
3838
cache = cacheBuilder.build();
3939
}
4040

41-
public IndexFieldMap getOrInitialize(Index index) {
41+
IndexFieldMap getOrInitialize(Index index) {
4242
try {
4343
return cache.computeIfAbsent(index, k -> new IndexFieldMap());
4444
} catch (ExecutionException ex) {
@@ -59,9 +59,33 @@ public Iterable<Index> keySet() {
5959
return cache.keys();
6060
}
6161

62+
/**
63+
* Estimated memory consumption of the cache in bytes
64+
*/
65+
public Long getEstimatedSize() {
66+
long totalWeight = 0;
67+
68+
// Iterate over the keys of the cache
69+
for (Index index : cache.keys()) {
70+
// Get the corresponding IndexFieldMap
71+
IndexFieldMap indexFieldMap = cache.get(index);
72+
73+
// Ensure the value is not null before calling weight()
74+
if (indexFieldMap != null) {
75+
totalWeight += indexFieldMap.weight();
76+
}
77+
}
78+
79+
return totalWeight;
80+
}
81+
6282
static class IndexFieldMap {
63-
private ConcurrentHashMap<String, String> fieldTypeMap;
64-
private CounterMetric weight;
83+
private final ConcurrentHashMap<String, String> fieldTypeMap;
84+
85+
/**
86+
* Estimated memory consumption of fieldTypeMap in bytes
87+
*/
88+
private final CounterMetric weight;
6589

6690
IndexFieldMap() {
6791
fieldTypeMap = new ConcurrentHashMap<>();

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

+21-1
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,21 @@ 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 HITS_STRING = "hits";
47+
public static final String MISSES_STRING = "misses";
48+
public static final String BYTES_STRING = "bytes";
4349

4450
public QueryShapeGenerator(ClusterService clusterService) {
4551
this.clusterService = clusterService;
4652
clusterService.addListener(this);
4753
this.indicesFieldTypeCache = new IndicesFieldTypeCache(clusterService.getSettings());
54+
this.cacheHitCount = 0;
55+
this.cacheMissCount = 0;
4856
}
4957

5058
public void clusterChanged(ClusterChangedEvent event) {
@@ -369,7 +377,10 @@ String getFieldType(String fieldName, Map<String, Object> propertiesAsMap, Index
369377
String fieldType = getFieldTypeFromCache(fieldName, index);
370378

371379
if (fieldType != null) {
380+
cacheHitCount += 1;
372381
return fieldType;
382+
} else {
383+
cacheMissCount += 1;
373384
}
374385

375386
// Retrieve field type from mapping and cache it if found
@@ -420,4 +431,13 @@ else if (currentMap.containsKey("type")) {
420431
String getFieldTypeFromCache(String fieldName, Index index) {
421432
return indicesFieldTypeCache.getOrInitialize(index).get(fieldName);
422433
}
434+
435+
/**
436+
* Get field type cache stats
437+
*
438+
* @return Map containing cache hit count, miss count, and byte stats
439+
*/
440+
public Map<String, Long> getFieldTypeCacheStats() {
441+
return Map.of(HITS_STRING, cacheHitCount, MISSES_STRING, cacheMissCount, BYTES_STRING, indicesFieldTypeCache.getEstimatedSize());
442+
}
423443
}

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

+28-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99
package org.opensearch.plugin.insights.rules.model.healthStats;
1010

1111
import java.io.IOException;
12+
import java.util.List;
1213
import java.util.Map;
14+
import org.opensearch.Version;
1315
import org.opensearch.core.common.io.stream.StreamInput;
1416
import org.opensearch.core.common.io.stream.StreamOutput;
1517
import org.opensearch.core.common.io.stream.Writeable;
@@ -26,10 +28,12 @@ public class QueryInsightsHealthStats implements ToXContentFragment, Writeable {
2628
private final ThreadPool.Info threadPoolInfo;
2729
private final int queryRecordsQueueSize;
2830
private final Map<MetricType, TopQueriesHealthStats> topQueriesHealthStats;
31+
private Map<String, Long> fieldTypeCacheStats;
2932

3033
private static final String THREAD_POOL_INFO = "ThreadPoolInfo";
3134
private static final String QUERY_RECORDS_QUEUE_SIZE = "QueryRecordsQueueSize";
3235
private static final String TOP_QUERIES_HEALTH_STATS = "TopQueriesHealthStats";
36+
private static final String FIELD_TYPE_CACHE_STATS = "FieldTypeCacheStats";
3337

3438
/**
3539
* Constructor to read QueryInsightsHealthStats from a StreamInput.
@@ -41,6 +45,9 @@ public QueryInsightsHealthStats(final StreamInput in) throws IOException {
4145
this.threadPoolInfo = new ThreadPool.Info(in);
4246
this.queryRecordsQueueSize = in.readInt();
4347
this.topQueriesHealthStats = in.readMap(MetricType::readFromStream, TopQueriesHealthStats::new);
48+
if (in.getVersion().onOrAfter(Version.V_2_19_0)) {
49+
this.fieldTypeCacheStats = in.readMap(StreamInput::readString, StreamInput::readLong);
50+
}
4451
}
4552

4653
/**
@@ -53,14 +60,16 @@ public QueryInsightsHealthStats(final StreamInput in) throws IOException {
5360
public QueryInsightsHealthStats(
5461
final ThreadPool.Info threadPoolInfo,
5562
final int queryRecordsQueueSize,
56-
final Map<MetricType, TopQueriesHealthStats> topQueriesHealthStats
63+
final Map<MetricType, TopQueriesHealthStats> topQueriesHealthStats,
64+
final Map<String, Long> fieldTypeCacheStats
5765
) {
5866
if (threadPoolInfo == null || topQueriesHealthStats == null) {
5967
throw new IllegalArgumentException("Parameters cannot be null");
6068
}
6169
this.threadPoolInfo = threadPoolInfo;
6270
this.queryRecordsQueueSize = queryRecordsQueueSize;
6371
this.topQueriesHealthStats = topQueriesHealthStats;
72+
this.fieldTypeCacheStats = fieldTypeCacheStats;
6473
}
6574

6675
/**
@@ -87,6 +96,12 @@ public XContentBuilder toXContent(final XContentBuilder builder, final Params pa
8796
builder.endObject();
8897
}
8998
builder.endObject();
99+
// Write field type cache stats
100+
builder.startObject(FIELD_TYPE_CACHE_STATS);
101+
for (String key : List.of("hits", "misses", "bytes")) {
102+
builder.field(key, fieldTypeCacheStats.getOrDefault(key, 0L));
103+
}
104+
builder.endObject();
90105
return builder;
91106
}
92107

@@ -105,6 +120,9 @@ public void writeTo(final StreamOutput out) throws IOException {
105120
MetricType::writeTo,
106121
(streamOutput, topQueriesHealthStats) -> topQueriesHealthStats.writeTo(out)
107122
);
123+
if (out.getVersion().onOrAfter(Version.V_2_19_0)) {
124+
out.writeMap(fieldTypeCacheStats, StreamOutput::writeString, StreamOutput::writeLong);
125+
}
108126
}
109127

110128
/**
@@ -133,4 +151,13 @@ public int getQueryRecordsQueueSize() {
133151
public Map<MetricType, TopQueriesHealthStats> getTopQueriesHealthStats() {
134152
return topQueriesHealthStats;
135153
}
154+
155+
/**
156+
* Get the field type cache stats.
157+
*
158+
* @return the field type cache stats
159+
*/
160+
public Map<String, Long> getFieldTypeCacheStats() {
161+
return fieldTypeCacheStats;
162+
}
136163
}

src/test/java/org/opensearch/plugin/insights/core/service/QueryInsightsServiceTests.java

+9
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
import static org.mockito.Mockito.mock;
1313
import static org.mockito.Mockito.spy;
1414
import static org.mockito.Mockito.when;
15+
import static org.opensearch.plugin.insights.core.service.categorizer.QueryShapeGenerator.BYTES_STRING;
16+
import static org.opensearch.plugin.insights.core.service.categorizer.QueryShapeGenerator.HITS_STRING;
17+
import static org.opensearch.plugin.insights.core.service.categorizer.QueryShapeGenerator.MISSES_STRING;
1518

1619
import java.util.List;
1720
import java.util.Map;
@@ -207,5 +210,11 @@ public void testGetHealthStats() {
207210
assertTrue(topQueriesHealthStatsMap.containsKey(MetricType.LATENCY));
208211
assertTrue(topQueriesHealthStatsMap.containsKey(MetricType.CPU));
209212
assertTrue(topQueriesHealthStatsMap.containsKey(MetricType.MEMORY));
213+
Map<String, Long> fieldTypeCacheStats = healthStats.getFieldTypeCacheStats();
214+
assertNotNull(fieldTypeCacheStats);
215+
assertEquals(3, fieldTypeCacheStats.size());
216+
assertTrue(fieldTypeCacheStats.containsKey(HITS_STRING));
217+
assertTrue(fieldTypeCacheStats.containsKey(MISSES_STRING));
218+
assertTrue(fieldTypeCacheStats.containsKey(BYTES_STRING));
210219
}
211220
}

src/test/java/org/opensearch/plugin/insights/rules/action/health_stats/HealthStatsNodeResponseTests.java

+1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ public void setup() {
5353
this.healthStats = new QueryInsightsHealthStats(
5454
threadPool.info(QueryInsightsSettings.QUERY_INSIGHTS_EXECUTOR),
5555
10,
56+
new HashMap<>(),
5657
new HashMap<>()
5758
);
5859
}

src/test/java/org/opensearch/plugin/insights/rules/action/health_stats/HealthStatsResponseTests.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ public void setup() {
5858
this.healthStats = new QueryInsightsHealthStats(
5959
threadPool.info(QueryInsightsSettings.QUERY_INSIGHTS_EXECUTOR),
6060
10,
61+
new HashMap<>(),
6162
new HashMap<>()
6263
);
6364
}
@@ -113,7 +114,7 @@ public void testToXContent() throws IOException {
113114
XContentBuilder builder = XContentFactory.jsonBuilder();
114115
response.toXContent(builder, ToXContent.EMPTY_PARAMS);
115116
String expectedJson =
116-
"{\"node_for_health_stats_test\":{\"ThreadPoolInfo\":{\"query_insights_executor\":{\"type\":\"scaling\",\"core\":1,\"max\":5,\"keep_alive\":\"5m\",\"queue_size\":-1}},\"QueryRecordsQueueSize\":10,\"TopQueriesHealthStats\":{}}}";
117+
"{\"node_for_health_stats_test\":{\"ThreadPoolInfo\":{\"query_insights_executor\":{\"type\":\"scaling\",\"core\":1,\"max\":5,\"keep_alive\":\"5m\",\"queue_size\":-1}},\"QueryRecordsQueueSize\":10,\"TopQueriesHealthStats\":{},\"FieldTypeCacheStats\":{\"hits\":0,\"misses\":0,\"bytes\":0}}}";
117118
assertEquals(expectedJson, builder.toString());
118119
}
119120
}

src/test/java/org/opensearch/plugin/insights/rules/model/healthStats/QueryInsightsHealthStatsTests.java

+29-3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88

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

11+
import static org.opensearch.plugin.insights.core.service.categorizer.QueryShapeGenerator.BYTES_STRING;
12+
import static org.opensearch.plugin.insights.core.service.categorizer.QueryShapeGenerator.HITS_STRING;
13+
import static org.opensearch.plugin.insights.core.service.categorizer.QueryShapeGenerator.MISSES_STRING;
14+
1115
import java.io.IOException;
1216
import java.util.HashMap;
1317
import java.util.Map;
@@ -34,6 +38,7 @@ public class QueryInsightsHealthStatsTests extends OpenSearchTestCase {
3438
private ThreadPool.Info threadPoolInfo;
3539
private int queryRecordsQueueSize;
3640
private Map<MetricType, TopQueriesHealthStats> topQueriesHealthStats;
41+
private Map<String, Long> fieldTypeCacheStats;
3742

3843
@Before
3944
public void setUpQueryInsightsHealthStats() {
@@ -45,6 +50,7 @@ public void setUpQueryInsightsHealthStats() {
4550
queryRecordsQueueSize = 100;
4651
topQueriesHealthStats = new HashMap<>();
4752
topQueriesHealthStats.put(MetricType.LATENCY, new TopQueriesHealthStats(10, new QueryGrouperHealthStats(20, 15)));
53+
fieldTypeCacheStats = Map.of(HITS_STRING, 5L, MISSES_STRING, 3L, BYTES_STRING, 300L);
4854
}
4955

5056
@Override
@@ -54,15 +60,25 @@ public void tearDown() throws Exception {
5460
}
5561

5662
public void testConstructorAndGetters() {
57-
QueryInsightsHealthStats healthStats = new QueryInsightsHealthStats(threadPoolInfo, queryRecordsQueueSize, topQueriesHealthStats);
63+
QueryInsightsHealthStats healthStats = new QueryInsightsHealthStats(
64+
threadPoolInfo,
65+
queryRecordsQueueSize,
66+
topQueriesHealthStats,
67+
fieldTypeCacheStats
68+
);
5869
assertNotNull(healthStats);
5970
assertEquals(threadPoolInfo, healthStats.getThreadPoolInfo());
6071
assertEquals(queryRecordsQueueSize, healthStats.getQueryRecordsQueueSize());
6172
assertEquals(topQueriesHealthStats, healthStats.getTopQueriesHealthStats());
6273
}
6374

6475
public void testSerialization() throws IOException {
65-
QueryInsightsHealthStats healthStats = new QueryInsightsHealthStats(threadPoolInfo, queryRecordsQueueSize, topQueriesHealthStats);
76+
QueryInsightsHealthStats healthStats = new QueryInsightsHealthStats(
77+
threadPoolInfo,
78+
queryRecordsQueueSize,
79+
topQueriesHealthStats,
80+
fieldTypeCacheStats
81+
);
6682
// Write to StreamOutput
6783
BytesStreamOutput out = new BytesStreamOutput();
6884
healthStats.writeTo(out);
@@ -75,7 +91,12 @@ public void testSerialization() throws IOException {
7591
}
7692

7793
public void testToXContent() throws IOException {
78-
QueryInsightsHealthStats healthStats = new QueryInsightsHealthStats(threadPoolInfo, queryRecordsQueueSize, topQueriesHealthStats);
94+
QueryInsightsHealthStats healthStats = new QueryInsightsHealthStats(
95+
threadPoolInfo,
96+
queryRecordsQueueSize,
97+
topQueriesHealthStats,
98+
fieldTypeCacheStats
99+
);
79100
XContentBuilder builder = XContentFactory.jsonBuilder();
80101
builder.startObject();
81102

@@ -100,6 +121,11 @@ public void testToXContent() throws IOException {
100121
+ " \"QueryGroupCount_Total\": 20,\n"
101122
+ " \"QueryGroupCount_MaxHeap\": 15\n"
102123
+ " }\n"
124+
+ " },\n"
125+
+ " \"FieldTypeCacheStats\": {\n"
126+
+ " \"hits\": 5,\n"
127+
+ " \"misses\": 3,\n"
128+
+ " \"bytes\": 300\n"
103129
+ " }\n"
104130
+ "}";
105131
assertEquals(expectedJson.replaceAll("\\s", ""), jsonOutput.replaceAll("\\s", ""));

0 commit comments

Comments
 (0)