Skip to content

Commit 1352627

Browse files
authored
[Star-Tree] Search level stats for nodes, indices and shards (#18707)
* startree node,indices,shards stats Signed-off-by: Sandesh Kumar <[email protected]> * changing to builder pattern Signed-off-by: Sandesh Kumar <[email protected]> * add query current, fix version Signed-off-by: Sandesh Kumar <[email protected]> * fix version Signed-off-by: Sandesh Kumar <[email protected]> * add integ test Signed-off-by: Sandesh Kumar <[email protected]> * typo Signed-off-by: Sandesh Kumar <[email protected]> --------- Signed-off-by: Sandesh Kumar <[email protected]>
1 parent fc6b08e commit 1352627

File tree

10 files changed

+664
-34
lines changed

10 files changed

+664
-34
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
2929
- Include named queries from rescore contexts in matched_queries array ([#18697](https://github.com/opensearch-project/OpenSearch/pull/18697))
3030
- Add the configurable limit on rule cardinality ([#18663](https://github.com/opensearch-project/OpenSearch/pull/18663))
3131
- [Experimental] Start in "clusterless" mode if a clusterless ClusterPlugin is loaded ([#18479](https://github.com/opensearch-project/OpenSearch/pull/18479))
32+
- [Star-Tree] Add star-tree search related stats ([#18707](https://github.com/opensearch-project/OpenSearch/pull/18707))
3233

3334
### Changed
3435
- Update Subject interface to use CheckedRunnable ([#18570](https://github.com/opensearch-project/OpenSearch/issues/18570))

rest-api-spec/src/main/resources/rest-api-spec/test/cat.shards/10_basic.yml

Lines changed: 101 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,111 @@
11
"Help":
22
- skip:
3-
version: " - 2.13.99"
4-
reason: search idle reactivate count total is only added in 3.0.0
3+
version: " - 3.1.99"
4+
reason: star-tree search stats is only added in 3.2.0
55
features: node_selector
66
- do:
77
cat.shards:
88
help: true
99
node_selector:
10-
version: "2.14.0 - "
10+
version: "3.2.0 - "
11+
12+
- match:
13+
$body: |
14+
/^ index .+ \n
15+
shard .+ \n
16+
prirep .+ \n
17+
state .+ \n
18+
docs .+ \n
19+
store .+ \n
20+
ip .+ \n
21+
id .+ \n
22+
node .+ \n
23+
sync_id .+ \n
24+
unassigned.reason .+ \n
25+
unassigned.at .+ \n
26+
unassigned.for .+ \n
27+
unassigned.details .+ \n
28+
recoverysource.type .+ \n
29+
completion.size .+ \n
30+
fielddata.memory_size .+ \n
31+
fielddata.evictions .+ \n
32+
query_cache.memory_size .+ \n
33+
query_cache.evictions .+ \n
34+
flush.total .+ \n
35+
flush.total_time .+ \n
36+
get.current .+ \n
37+
get.time .+ \n
38+
get.total .+ \n
39+
get.exists_time .+ \n
40+
get.exists_total .+ \n
41+
get.missing_time .+ \n
42+
get.missing_total .+ \n
43+
indexing.delete_current .+ \n
44+
indexing.delete_time .+ \n
45+
indexing.delete_total .+ \n
46+
indexing.index_current .+ \n
47+
indexing.index_time .+ \n
48+
indexing.index_total .+ \n
49+
indexing.index_failed .+ \n
50+
merges.current .+ \n
51+
merges.current_docs .+ \n
52+
merges.current_size .+ \n
53+
merges.total .+ \n
54+
merges.total_docs .+ \n
55+
merges.total_size .+ \n
56+
merges.total_time .+ \n
57+
refresh.total .+ \n
58+
refresh.time .+ \n
59+
refresh.external_total .+ \n
60+
refresh.external_time .+ \n
61+
refresh.listeners .+ \n
62+
search.fetch_current .+ \n
63+
search.fetch_time .+ \n
64+
search.fetch_total .+ \n
65+
search.open_contexts .+ \n
66+
search.query_current .+ \n
67+
search.query_time .+ \n
68+
search.query_total .+ \n
69+
search.concurrent_query_current .+ \n
70+
search.concurrent_query_time .+ \n
71+
search.concurrent_query_total .+ \n
72+
search.concurrent_avg_slice_count .+ \n
73+
search.startree_query_current .+ \n
74+
search.startree_query_time .+ \n
75+
search.startree_query_total .+ \n
76+
search.scroll_current .+ \n
77+
search.scroll_time .+ \n
78+
search.scroll_total .+ \n
79+
search.point_in_time_current .+ \n
80+
search.point_in_time_time .+ \n
81+
search.point_in_time_total .+ \n
82+
search.search_idle_reactivate_count_total .+ \n
83+
segments.count .+ \n
84+
segments.memory .+ \n
85+
segments.index_writer_memory .+ \n
86+
segments.version_map_memory .+ \n
87+
segments.fixed_bitset_memory .+ \n
88+
seq_no.max .+ \n
89+
seq_no.local_checkpoint .+ \n
90+
seq_no.global_checkpoint .+ \n
91+
warmer.current .+ \n
92+
warmer.total .+ \n
93+
warmer.total_time .+ \n
94+
path.data .+ \n
95+
path.state .+ \n
96+
docs.deleted .+ \n
97+
$/
98+
---
99+
"Help from 2.14.0 to 3.0.99":
100+
- skip:
101+
version: " - 2.13.99, 3.2.0 - "
102+
reason: search idle reactivate count total is only added in 3.0.0
103+
features: node_selector
104+
- do:
105+
cat.shards:
106+
help: true
107+
node_selector:
108+
version: "2.14.0 - 3.1.99"
11109

12110
- match:
13111
$body: |
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
*
4+
* The OpenSearch Contributors require contributions made to
5+
* this file be licensed under the Apache-2.0 license or a
6+
* compatible open source license.
7+
*/
8+
9+
package org.opensearch.search.stats;
10+
11+
import org.opensearch.action.admin.indices.forcemerge.ForceMergeRequest;
12+
import org.opensearch.action.admin.indices.stats.IndicesStatsResponse;
13+
import org.opensearch.action.search.SearchResponse;
14+
import org.opensearch.cluster.metadata.IndexMetadata;
15+
import org.opensearch.common.settings.Settings;
16+
import org.opensearch.common.xcontent.XContentFactory;
17+
import org.opensearch.index.search.stats.SearchStats;
18+
import org.opensearch.search.aggregations.AggregationBuilders;
19+
import org.opensearch.search.aggregations.bucket.terms.Terms;
20+
import org.opensearch.search.aggregations.metrics.Sum;
21+
import org.opensearch.test.OpenSearchIntegTestCase;
22+
23+
import static org.opensearch.index.query.QueryBuilders.matchAllQuery;
24+
import static org.opensearch.index.query.QueryBuilders.termQuery;
25+
import static org.hamcrest.Matchers.equalTo;
26+
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
27+
import static org.hamcrest.Matchers.lessThanOrEqualTo;
28+
29+
/**
30+
* Integration tests for search statistics related to star-tree.
31+
*/
32+
@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.SUITE)
33+
public class StarTreeSearchStatsIT extends OpenSearchIntegTestCase {
34+
35+
private static final String STARTREE_INDEX_NAME = "test_startree";
36+
private static final String REGULAR_INDEX_NAME = "test_regular";
37+
38+
/**
39+
* This test validates that star-tree specific search stats are correctly reported.
40+
* It creates two indices: one with a star-tree field and one without.
41+
* It then runs queries that should and should not be offloaded to the star-tree
42+
* and verifies the star-tree related stats.
43+
*/
44+
public void testStarTreeQueryStats() throws Exception {
45+
// Create an index with a star-tree field mapping using the composite structure
46+
createIndex(
47+
STARTREE_INDEX_NAME,
48+
Settings.builder()
49+
.put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1)
50+
.put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0)
51+
.put("index.composite_index", true)
52+
.put("index.append_only.enabled", true)
53+
.put("index.search.star_tree_index.enabled", true)
54+
.put("index.codec", "default")
55+
.build(),
56+
XContentFactory.jsonBuilder()
57+
.startObject()
58+
.startObject("composite")
59+
.startObject("my_star_tree")
60+
.field("type", "star_tree")
61+
.startObject("config")
62+
.startArray("ordered_dimensions")
63+
.startObject()
64+
.field("name", "product")
65+
.endObject()
66+
.startObject()
67+
.field("name", "size")
68+
.endObject()
69+
.endArray()
70+
.startArray("metrics")
71+
.startObject()
72+
.field("name", "sales")
73+
.field("stats", new String[] { "sum" })
74+
.endObject()
75+
.endArray()
76+
.endObject()
77+
.endObject()
78+
.endObject()
79+
.startObject("properties")
80+
.startObject("product")
81+
.field("type", "keyword")
82+
.endObject()
83+
.startObject("size")
84+
.field("type", "keyword")
85+
.endObject()
86+
.startObject("sales")
87+
.field("type", "double")
88+
.endObject()
89+
.startObject("non_dimension_field")
90+
.field("type", "text")
91+
.endObject()
92+
.endObject()
93+
.endObject()
94+
.toString()
95+
);
96+
97+
// Create an index without a star-tree
98+
createIndex(
99+
REGULAR_INDEX_NAME,
100+
Settings.builder().put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 1).put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 0).build()
101+
);
102+
ensureGreen(STARTREE_INDEX_NAME, REGULAR_INDEX_NAME);
103+
104+
client().prepareIndex(STARTREE_INDEX_NAME)
105+
.setSource("product", "laptop", "sales", 1200.0, "non_dimension_field", "some text", "size", "18")
106+
.get();
107+
client().prepareIndex(STARTREE_INDEX_NAME)
108+
.setSource("product", "keyboard", "sales", 150.0, "non_dimension_field", "more text", "size", "12")
109+
.get();
110+
client().prepareIndex(REGULAR_INDEX_NAME).setSource("product", "monitor", "sales", 400.0).get();
111+
112+
// Force merge the star-tree index to ensure star-tree segments are created
113+
client().admin().indices().forceMerge(new ForceMergeRequest(STARTREE_INDEX_NAME).maxNumSegments(1)).get();
114+
refresh();
115+
116+
// Query 1: This query should be answered by the star-tree.
117+
// It's an aggregation on a dimension with a metric sub-aggregation and size=0.
118+
SearchResponse starTreeResponse = client().prepareSearch(STARTREE_INDEX_NAME)
119+
.setQuery(matchAllQuery())
120+
.addAggregation(
121+
AggregationBuilders.terms("products").field("product").subAggregation(AggregationBuilders.sum("total_sales").field("sales"))
122+
)
123+
.setSize(0)
124+
.execute()
125+
.actionGet();
126+
assertEquals(2, starTreeResponse.getHits().getTotalHits().value());
127+
Terms terms = starTreeResponse.getAggregations().get("products");
128+
assertEquals(2, terms.getBuckets().size());
129+
Sum sum = terms.getBuckets().get(0).getAggregations().get("total_sales");
130+
assertNotNull(sum);
131+
132+
// Query 2: This query should NOT be answered by the star-tree as it queries a non-dimension field.
133+
client().prepareSearch(STARTREE_INDEX_NAME).setQuery(termQuery("non_dimension_field", "text")).execute().actionGet();
134+
135+
// Query 3: A standard query on the regular index.
136+
client().prepareSearch(REGULAR_INDEX_NAME).setQuery(matchAllQuery()).get();
137+
138+
// Fetch index statistics
139+
IndicesStatsResponse stats = client().admin().indices().prepareStats(STARTREE_INDEX_NAME, REGULAR_INDEX_NAME).setSearch(true).get();
140+
141+
// Assertions for the star-tree index
142+
SearchStats.Stats starTreeIndexStats = stats.getIndex(STARTREE_INDEX_NAME).getTotal().getSearch().getTotal();
143+
144+
assertThat("Star-tree index should have 2 total queries", starTreeIndexStats.getQueryCount(), equalTo(2L));
145+
assertThat("Star-tree index should have 1 star-tree query", starTreeIndexStats.getStarTreeQueryCount(), equalTo(1L));
146+
assertThat(
147+
"Star-tree query time should be non-negative",
148+
starTreeIndexStats.getStarTreeQueryTimeInMillis(),
149+
greaterThanOrEqualTo(0L)
150+
);
151+
assertThat(
152+
"Star-tree query current should be non-negative",
153+
starTreeIndexStats.getStarTreeQueryCurrent(),
154+
greaterThanOrEqualTo(0L)
155+
);
156+
157+
// Assertions for the regular index
158+
SearchStats.Stats regularIndexStats = stats.getIndex(REGULAR_INDEX_NAME).getTotal().getSearch().getTotal();
159+
assertThat("Regular index should have 1 total query", regularIndexStats.getQueryCount(), equalTo(1L));
160+
assertThat("Regular index should have 0 star-tree queries", regularIndexStats.getStarTreeQueryCount(), equalTo(0L));
161+
assertThat("Regular index should have 0 star-tree query time", regularIndexStats.getStarTreeQueryTimeInMillis(), equalTo(0L));
162+
163+
// Assertions for the total stats across both indices
164+
SearchStats.Stats totalStats = stats.getTotal().getSearch().getTotal();
165+
assertThat("Total query count should be 3", totalStats.getQueryCount(), equalTo(3L));
166+
assertThat("Total star-tree query count should be 1", totalStats.getStarTreeQueryCount(), equalTo(1L));
167+
assertThat(totalStats.getQueryTimeInMillis(), greaterThanOrEqualTo(totalStats.getStarTreeQueryTimeInMillis()));
168+
169+
// While not guaranteed, the time spent on the single star-tree query should be less than the total query time.
170+
if (totalStats.getStarTreeQueryCount() > 0 && totalStats.getQueryCount() > totalStats.getStarTreeQueryCount()) {
171+
assertThat(
172+
"Star-tree query time should be less than total query time",
173+
totalStats.getStarTreeQueryTimeInMillis(),
174+
lessThanOrEqualTo(totalStats.getQueryTimeInMillis())
175+
);
176+
}
177+
}
178+
}

0 commit comments

Comments
 (0)