Skip to content

Commit 03355c8

Browse files
committed
use index mapping and define default index metadata for top n queries
Signed-off-by: Chenyang Ji <cyji@amazon.com>
1 parent f6c028e commit 03355c8

File tree

4 files changed

+249
-20
lines changed

4 files changed

+249
-20
lines changed

src/main/java/org/opensearch/plugin/insights/core/exporter/LocalIndexExporter.java

+110-15
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,30 @@
1111
import static org.opensearch.plugin.insights.core.service.TopQueriesService.isTopQueriesIndex;
1212
import static org.opensearch.plugin.insights.settings.QueryInsightsSettings.DEFAULT_DELETE_AFTER_VALUE;
1313

14+
import java.io.IOException;
15+
import java.nio.charset.Charset;
1416
import java.time.Instant;
1517
import java.time.ZoneOffset;
1618
import java.time.ZonedDateTime;
1719
import java.time.format.DateTimeFormatter;
1820
import java.util.List;
1921
import java.util.Locale;
2022
import java.util.Map;
23+
import java.util.Objects;
2124
import java.util.concurrent.TimeUnit;
2225
import org.apache.logging.log4j.LogManager;
2326
import org.apache.logging.log4j.Logger;
27+
import org.opensearch.ResourceAlreadyExistsException;
28+
import org.opensearch.action.admin.indices.create.CreateIndexRequest;
29+
import org.opensearch.action.admin.indices.create.CreateIndexResponse;
2430
import org.opensearch.action.bulk.BulkRequestBuilder;
2531
import org.opensearch.action.bulk.BulkResponse;
2632
import org.opensearch.action.index.IndexRequest;
2733
import org.opensearch.client.Client;
34+
import org.opensearch.cluster.ClusterState;
2835
import org.opensearch.cluster.metadata.IndexMetadata;
36+
import org.opensearch.cluster.service.ClusterService;
37+
import org.opensearch.common.settings.Settings;
2938
import org.opensearch.common.unit.TimeValue;
3039
import org.opensearch.common.xcontent.XContentFactory;
3140
import org.opensearch.core.action.ActionListener;
@@ -44,19 +53,38 @@ public final class LocalIndexExporter implements QueryInsightsExporter {
4453
*/
4554
private final Logger logger = LogManager.getLogger();
4655
private final Client client;
56+
private final ClusterService clusterService;
57+
private final String indexMapping;
4758
private DateTimeFormatter indexPattern;
4859
private int deleteAfter;
4960
private final String id;
61+
private static final int DEFAULT_NUMBER_OF_REPLICA = 1;
62+
private static final int DEFAULT_NUMBER_OF_SHARDS = 1;
63+
private static final List<String> DEFAULT_SORTED_FIELDS = List.of(
64+
"measurements.latency.number",
65+
"measurements.cpu.number",
66+
"measurements.memory.number"
67+
);
68+
private static final List<String> DEFAULT_SORTED_ORDERS = List.of(
69+
"desc",
70+
"desc",
71+
"desc"
72+
);
5073

5174
/**
5275
* Constructor of LocalIndexExporter
5376
*
5477
* @param client OS client
78+
* @param clusterService cluster service
5579
* @param indexPattern the pattern of index to export to
80+
* @param indexMapping the index mapping file
81+
* @param id id of the exporter
5682
*/
57-
public LocalIndexExporter(final Client client, final DateTimeFormatter indexPattern, final String id) {
83+
public LocalIndexExporter(final Client client, final ClusterService clusterService, final DateTimeFormatter indexPattern, final String indexMapping, final String id) {
5884
this.indexPattern = indexPattern;
5985
this.client = client;
86+
this.clusterService = clusterService;
87+
this.indexMapping = indexMapping;
6088
this.deleteAfter = DEFAULT_DELETE_AFTER_VALUE;
6189
this.id = id;
6290
}
@@ -96,28 +124,72 @@ public void export(final List<SearchQueryRecord> records) {
96124
}
97125
try {
98126
final String indexName = buildLocalIndexName();
99-
final BulkRequestBuilder bulkRequestBuilder = client.prepareBulk().setTimeout(TimeValue.timeValueMinutes(1));
100-
for (SearchQueryRecord record : records) {
101-
bulkRequestBuilder.add(
102-
new IndexRequest(indexName).source(record.toXContent(XContentFactory.jsonBuilder(), ToXContent.EMPTY_PARAMS))
127+
if (!checkIndexExists(indexName)) {
128+
CreateIndexRequest createIndexRequest = new CreateIndexRequest(indexName);
129+
130+
createIndexRequest.settings(Settings.builder()
131+
.putList("index.sort.field", DEFAULT_SORTED_FIELDS)
132+
.putList("index.sort.order", DEFAULT_SORTED_ORDERS)
133+
.put("index.number_of_shards", DEFAULT_NUMBER_OF_SHARDS)
134+
.put("index.number_of_replicas", DEFAULT_NUMBER_OF_REPLICA)
103135
);
136+
createIndexRequest.mapping(readIndexMappings());
137+
138+
client.admin().indices().create(createIndexRequest, new ActionListener<>() {
139+
@Override
140+
public void onResponse(CreateIndexResponse createIndexResponse) {
141+
if (createIndexResponse.isAcknowledged()) {
142+
try {
143+
bulk(indexName, records);
144+
} catch (IOException e) {
145+
OperationalMetricsCounter.getInstance().incrementCounter(OperationalMetric.LOCAL_INDEX_EXPORTER_EXCEPTIONS);
146+
logger.error("Unable to index query insights data: ", e);
147+
}
148+
}
149+
}
150+
@Override
151+
public void onFailure(Exception e) {
152+
if (e instanceof ResourceAlreadyExistsException) {
153+
try {
154+
bulk(indexName, records);
155+
} catch (IOException ex) {
156+
OperationalMetricsCounter.getInstance().incrementCounter(OperationalMetric.LOCAL_INDEX_EXPORTER_EXCEPTIONS);
157+
logger.error("Unable to index query insights data: ", e);
158+
}
159+
} else {
160+
OperationalMetricsCounter.getInstance().incrementCounter(OperationalMetric.LOCAL_INDEX_EXPORTER_EXCEPTIONS);
161+
logger.error("Unable to create query insights index: ", e);
162+
}
163+
}
164+
});
165+
} else {
166+
bulk(indexName, records);
104167
}
105-
bulkRequestBuilder.execute(new ActionListener<BulkResponse>() {
106-
@Override
107-
public void onResponse(BulkResponse bulkItemResponses) {}
108-
109-
@Override
110-
public void onFailure(Exception e) {
111-
OperationalMetricsCounter.getInstance().incrementCounter(OperationalMetric.LOCAL_INDEX_EXPORTER_BULK_FAILURES);
112-
logger.error("Failed to execute bulk operation for query insights data: ", e);
113-
}
114-
});
115168
} catch (final Exception e) {
116169
OperationalMetricsCounter.getInstance().incrementCounter(OperationalMetric.LOCAL_INDEX_EXPORTER_EXCEPTIONS);
117170
logger.error("Unable to index query insights data: ", e);
118171
}
119172
}
120173

174+
private void bulk(final String indexName, final List<SearchQueryRecord> records) throws IOException {
175+
final BulkRequestBuilder bulkRequestBuilder = client.prepareBulk().setTimeout(TimeValue.timeValueMinutes(1));
176+
for (SearchQueryRecord record : records) {
177+
bulkRequestBuilder.add(
178+
new IndexRequest(indexName).id(record.getId()).source(record.toXContent(XContentFactory.jsonBuilder(), ToXContent.EMPTY_PARAMS))
179+
);
180+
}
181+
bulkRequestBuilder.execute(new ActionListener<BulkResponse>() {
182+
@Override
183+
public void onResponse(BulkResponse bulkItemResponses) {}
184+
185+
@Override
186+
public void onFailure(Exception e) {
187+
OperationalMetricsCounter.getInstance().incrementCounter(OperationalMetric.LOCAL_INDEX_EXPORTER_BULK_FAILURES);
188+
logger.error("Failed to execute bulk operation for query insights data: ", e);
189+
}
190+
});
191+
}
192+
121193
/**
122194
* Close the exporter sink
123195
*/
@@ -174,4 +246,27 @@ public static String generateLocalIndexDateHash() {
174246
// Generate a 5-digit numeric hash from the date's hashCode
175247
return String.format(Locale.ROOT, "%05d", (currentDate.hashCode() % 100000 + 100000) % 100000);
176248
}
249+
250+
/**
251+
* check if index exists
252+
* @return boolean
253+
*/
254+
private boolean checkIndexExists(String indexName) {
255+
ClusterState clusterState = clusterService.state();
256+
return clusterState.getRoutingTable().hasIndex(indexName);
257+
}
258+
259+
/**
260+
* get correlation rule index mappings
261+
* @return mappings of correlation rule index
262+
* @throws IOException IOException
263+
*/
264+
private String readIndexMappings() throws IOException {
265+
return new String(
266+
Objects.requireNonNull(LocalIndexExporter.class.getClassLoader().getResourceAsStream(indexMapping))
267+
.readAllBytes(),
268+
Charset.defaultCharset()
269+
);
270+
}
271+
177272
}

src/main/java/org/opensearch/plugin/insights/core/exporter/QueryInsightsExporterFactory.java

+10-4
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import org.apache.logging.log4j.LogManager;
1717
import org.apache.logging.log4j.Logger;
1818
import org.opensearch.client.Client;
19+
import org.opensearch.cluster.service.ClusterService;
1920
import org.opensearch.plugin.insights.core.metrics.OperationalMetric;
2021
import org.opensearch.plugin.insights.core.metrics.OperationalMetricsCounter;
2122

@@ -28,15 +29,18 @@ public class QueryInsightsExporterFactory {
2829
*/
2930
private final Logger logger = LogManager.getLogger();
3031
final private Client client;
32+
final private ClusterService clusterService;
3133
final private Map<String, QueryInsightsExporter> exporters;
3234

3335
/**
3436
* Constructor of QueryInsightsExporterFactory
3537
*
3638
* @param client OS client
39+
* @param clusterService cluster service
3740
*/
38-
public QueryInsightsExporterFactory(final Client client) {
41+
public QueryInsightsExporterFactory(final Client client, final ClusterService clusterService) {
3942
this.client = client;
43+
this.clusterService = clusterService;
4044
this.exporters = new HashMap<>();
4145
}
4246

@@ -64,13 +68,15 @@ public void validateExporterType(final String exporterType) throws IllegalArgume
6468
/**
6569
* Create an exporter based on provided parameters
6670
*
71+
* @param id id of the exporter
6772
* @param type The type of exporter to create
68-
* @param indexPattern the index pattern if creating a index exporter
73+
* @param indexPattern the index pattern if creating an index exporter
74+
* @param indexMapping index mapping file
6975
* @return QueryInsightsExporter the created exporter sink
7076
*/
71-
public QueryInsightsExporter createExporter(String id, SinkType type, String indexPattern) {
77+
public QueryInsightsExporter createExporter(String id, SinkType type, String indexPattern, String indexMapping) {
7278
if (SinkType.LOCAL_INDEX.equals(type)) {
73-
QueryInsightsExporter exporter = new LocalIndexExporter(client, DateTimeFormatter.ofPattern(indexPattern, Locale.ROOT), id);
79+
QueryInsightsExporter exporter = new LocalIndexExporter(client, clusterService, DateTimeFormatter.ofPattern(indexPattern, Locale.ROOT), indexMapping, id);
7480
this.exporters.put(id, exporter);
7581
return exporter;
7682
}

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -444,7 +444,8 @@ private void setExporterAndReader(final SinkType sinkType, final Map<String, Ind
444444
queryInsightsExporterFactory.createExporter(
445445
TOP_QUERIES_LOCAL_INDEX_EXPORTER_ID,
446446
sinkType,
447-
DEFAULT_TOP_N_QUERIES_INDEX_PATTERN
447+
DEFAULT_TOP_N_QUERIES_INDEX_PATTERN,
448+
"mappings/top-queries-record.json"
448449
);
449450
}
450451
} else {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
{
2+
"dynamic": true,
3+
"_meta": {
4+
"schema_version": 1,
5+
"query_insights_feature_space": "top_n_queries"
6+
},
7+
"properties": {
8+
"id" : {
9+
"type" : "text",
10+
"fields" : {
11+
"keyword" : {
12+
"type" : "keyword",
13+
"ignore_above" : 256
14+
}
15+
}
16+
},
17+
"node_id" : {
18+
"type" : "text",
19+
"fields" : {
20+
"keyword" : {
21+
"type" : "keyword",
22+
"ignore_above" : 256
23+
}
24+
}
25+
},
26+
"timestamp" : {
27+
"type" : "long"
28+
},
29+
"total_shards" : {
30+
"type" : "long"
31+
},
32+
"group_by" : {
33+
"type" : "text",
34+
"fields" : {
35+
"keyword" : {
36+
"type" : "keyword",
37+
"ignore_above" : 256
38+
}
39+
}
40+
},
41+
"phase_latency_map" : {
42+
"properties" : {
43+
"expand" : {
44+
"type" : "long"
45+
},
46+
"fetch" : {
47+
"type" : "long"
48+
},
49+
"query" : {
50+
"type" : "long"
51+
}
52+
}
53+
},
54+
"search_type" : {
55+
"type" : "text",
56+
"fields" : {
57+
"keyword" : {
58+
"type" : "keyword",
59+
"ignore_above" : 256
60+
}
61+
}
62+
},
63+
"task_resource_usages" : {
64+
"properties" : {
65+
"action" : {
66+
"type" : "text",
67+
"fields" : {
68+
"keyword" : {
69+
"type" : "keyword",
70+
"ignore_above" : 256
71+
}
72+
}
73+
},
74+
"nodeId" : {
75+
"type" : "text",
76+
"fields" : {
77+
"keyword" : {
78+
"type" : "keyword",
79+
"ignore_above" : 256
80+
}
81+
}
82+
},
83+
"parentTaskId" : {
84+
"type" : "long"
85+
},
86+
"taskId" : {
87+
"type" : "long"
88+
},
89+
"taskResourceUsage" : {
90+
"properties" : {
91+
"cpu_time_in_nanos" : {
92+
"type" : "long"
93+
},
94+
"memory_in_bytes" : {
95+
"type" : "long"
96+
}
97+
}
98+
}
99+
}
100+
},
101+
"measurements": {
102+
"properties": {
103+
"latency": {
104+
"properties": {
105+
"number": {
106+
"type": "double"
107+
}
108+
}
109+
},
110+
"cpu": {
111+
"properties": {
112+
"number": {
113+
"type": "double"
114+
}
115+
}
116+
},
117+
"memory": {
118+
"properties": {
119+
"number": {
120+
"type": "double"
121+
}
122+
}
123+
}
124+
}
125+
}
126+
}
127+
}

0 commit comments

Comments
 (0)