Skip to content

Commit 5823ad6

Browse files
authored
Adjust /_cat/templates not to request all metadata (#78829)
Today `GET /_cat/templates` retrieves the whole cluster metadata from the master, which includes all sorts of unnecessary junk and consumes significant resources. This commit reimplements these endpoints using `GetIndexTemplatesAction` and `GetComposableIndexTemplateAction` which are much more efficient. The docs for this API indicate that it accepts a comma-separated list of template names/patterns of the form `GET /_cat/templates/name1,name2` but in fact today it only accepts a single name or pattern. This commit also adds support for multiple names/patterns as the docs claim.
1 parent bcd75c3 commit 5823ad6

File tree

2 files changed

+291
-35
lines changed

2 files changed

+291
-35
lines changed
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
setup:
2+
- skip:
3+
features:
4+
- "allowed_warnings_regex"
5+
6+
- do:
7+
indices.put_template:
8+
name: test-legacy-1
9+
body:
10+
order: 12
11+
version: 3
12+
index_patterns: foo*
13+
14+
- do:
15+
indices.put_template:
16+
name: test-legacy-2
17+
body:
18+
order: 45
19+
version: 6
20+
index_patterns:
21+
- bar*
22+
- baz*
23+
24+
- do:
25+
cluster.put_component_template:
26+
name: test-component-template
27+
body:
28+
template:
29+
settings:
30+
number_of_shards: 1
31+
number_of_replicas: 0
32+
33+
- do:
34+
indices.put_index_template:
35+
name: test-composable-1
36+
body:
37+
index_patterns:
38+
- quux*
39+
priority: 78
40+
version: 9
41+
composed_of:
42+
- test-component-template
43+
allowed_warnings_regex:
44+
- ".*index template .* has index patterns .* matching patterns from existing older templates.*"
45+
46+
- do:
47+
indices.put_index_template:
48+
name: test-composable-2
49+
body:
50+
index_patterns:
51+
- gruly*
52+
priority: 99
53+
version: 1
54+
composed_of:
55+
- test-component-template
56+
allowed_warnings_regex:
57+
- ".*index template .* has index patterns .* matching patterns from existing older templates.*"
58+
---
59+
"Matching all templates":
60+
61+
- do:
62+
cat.templates:
63+
h: [name]
64+
s: [name]
65+
66+
- match:
67+
$body: /test-composable-1\ntest-composable-2\ntest-legacy-1\ntest-legacy-2\n/
68+
69+
- do:
70+
cat.templates:
71+
name: "*"
72+
h: [name]
73+
s: [name]
74+
75+
- match:
76+
$body: /test-composable-1\ntest-composable-2\ntest-legacy-1\ntest-legacy-2\n/
77+
78+
---
79+
"Matching all templates with other patterns":
80+
- skip:
81+
version: " - 7.99.99"
82+
reason: "support for multiple patterns added in 8.0.0"
83+
84+
- do:
85+
cat.templates:
86+
name: "nonexistent*,*,other-name"
87+
h: [name]
88+
s: [name]
89+
90+
- match:
91+
$body: /test-composable-1\ntest-composable-2\ntest-legacy-1\ntest-legacy-2\n/
92+
93+
---
94+
"Matching no templates":
95+
96+
- do:
97+
cat.templates:
98+
name: "nonexistent"
99+
h: [name]
100+
s: [name]
101+
102+
- match:
103+
$body: /^$/
104+
105+
---
106+
"Matching single names":
107+
108+
- do:
109+
cat.templates:
110+
name: "test-legacy-1"
111+
h: [name]
112+
s: [name]
113+
114+
- match:
115+
$body: /^test-legacy-1\n$/
116+
117+
118+
- do:
119+
cat.templates:
120+
name: "test-composable-2"
121+
h: [name]
122+
s: [name]
123+
124+
- match:
125+
$body: /^test-composable-2\n$/
126+
127+
---
128+
"Matching single patterns":
129+
130+
- do:
131+
cat.templates:
132+
name: "test-legacy-*"
133+
h: [name]
134+
s: [name]
135+
136+
- match:
137+
$body: /^test-legacy-1\ntest-legacy-2\n$/
138+
139+
140+
- do:
141+
cat.templates:
142+
name: "test-*-2"
143+
h: [name]
144+
s: [name]
145+
146+
- match:
147+
$body: /^test-composable-2\ntest-legacy-2\n$/
148+
149+
---
150+
"Matching lists of names":
151+
- skip:
152+
version: " - 7.99.99"
153+
reason: "support for multiple patterns added in 8.0.0"
154+
155+
- do:
156+
cat.templates:
157+
name: "test-legacy-1,test-composable-2"
158+
h: [name]
159+
s: [name]
160+
161+
- match:
162+
$body: /^test-composable-2\ntest-legacy-1\n$/
163+
164+
---
165+
"Matching names and wildcards":
166+
- skip:
167+
version: " - 7.99.99"
168+
reason: "support for multiple patterns added in 8.0.0"
169+
170+
- do:
171+
cat.templates:
172+
name: "test-legacy-1,test-composable-*"
173+
h: [name]
174+
s: [name]
175+
176+
- match:
177+
$body: /^test-composable-1\ntest-composable-2\ntest-legacy-1\n$/
178+
179+
- do:
180+
cat.templates:
181+
name: "test-legacy-*,test-composable-2"
182+
h: [name]
183+
s: [name]
184+
185+
- match:
186+
$body: /^test-composable-2\ntest-legacy-1\ntest-legacy-2\n$/

server/src/main/java/org/elasticsearch/rest/action/cat/RestTemplatesAction.java

Lines changed: 105 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,27 @@
88

99
package org.elasticsearch.rest.action.cat;
1010

11-
import com.carrotsearch.hppc.cursors.ObjectObjectCursor;
12-
import org.elasticsearch.action.admin.cluster.state.ClusterStateRequest;
13-
import org.elasticsearch.action.admin.cluster.state.ClusterStateResponse;
11+
import org.elasticsearch.action.ActionListener;
12+
import org.elasticsearch.action.StepListener;
13+
import org.elasticsearch.action.admin.indices.template.get.GetComposableIndexTemplateAction;
14+
import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesRequest;
15+
import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesResponse;
1416
import org.elasticsearch.client.node.NodeClient;
15-
import org.elasticsearch.cluster.metadata.IndexTemplateMetadata;
1617
import org.elasticsearch.cluster.metadata.ComposableIndexTemplate;
17-
import org.elasticsearch.cluster.metadata.Metadata;
18+
import org.elasticsearch.cluster.metadata.IndexTemplateMetadata;
19+
import org.elasticsearch.common.Strings;
1820
import org.elasticsearch.common.Table;
1921
import org.elasticsearch.common.regex.Regex;
2022
import org.elasticsearch.rest.RestRequest;
2123
import org.elasticsearch.rest.RestResponse;
2224
import org.elasticsearch.rest.action.RestResponseListener;
2325

26+
import java.util.ArrayList;
27+
import java.util.HashSet;
2428
import java.util.List;
2529
import java.util.Map;
30+
import java.util.Set;
31+
import java.util.function.Predicate;
2632

2733
import static org.elasticsearch.rest.RestRequest.Method.GET;
2834

@@ -47,18 +53,43 @@ protected void documentation(StringBuilder sb) {
4753

4854
@Override
4955
protected RestChannelConsumer doCatRequest(final RestRequest request, NodeClient client) {
50-
final String matchPattern = request.hasParam("name") ? request.param("name") : null;
51-
final ClusterStateRequest clusterStateRequest = new ClusterStateRequest();
52-
clusterStateRequest.clear().metadata(true);
53-
clusterStateRequest.local(request.paramAsBoolean("local", clusterStateRequest.local()));
54-
clusterStateRequest.masterNodeTimeout(request.paramAsTime("master_timeout", clusterStateRequest.masterNodeTimeout()));
55-
56-
return channel -> client.admin().cluster().state(clusterStateRequest, new RestResponseListener<ClusterStateResponse>(channel) {
57-
@Override
58-
public RestResponse buildResponse(ClusterStateResponse clusterStateResponse) throws Exception {
59-
return RestTable.buildResponse(buildTable(request, clusterStateResponse, matchPattern), channel);
60-
}
61-
});
56+
final String[] templateNames = Strings.splitStringByCommaToArray(request.param("name", ""));
57+
58+
final GetIndexTemplatesRequest getIndexTemplatesRequest = new GetIndexTemplatesRequest(templateNames);
59+
getIndexTemplatesRequest.local(request.paramAsBoolean("local", getIndexTemplatesRequest.local()));
60+
getIndexTemplatesRequest.masterNodeTimeout(request.paramAsTime("master_timeout", getIndexTemplatesRequest.masterNodeTimeout()));
61+
62+
final GetComposableIndexTemplateAction.Request getComposableTemplatesRequest
63+
= new GetComposableIndexTemplateAction.Request();
64+
getComposableTemplatesRequest.local(request.paramAsBoolean("local", getComposableTemplatesRequest.local()));
65+
getComposableTemplatesRequest.masterNodeTimeout(
66+
request.paramAsTime("master_timeout", getComposableTemplatesRequest.masterNodeTimeout()));
67+
68+
return channel -> {
69+
70+
final StepListener<GetIndexTemplatesResponse> getIndexTemplatesStep = new StepListener<>();
71+
client.admin().indices().getTemplates(getIndexTemplatesRequest, getIndexTemplatesStep);
72+
73+
final StepListener<GetComposableIndexTemplateAction.Response> getComposableTemplatesStep = new StepListener<>();
74+
client.execute(GetComposableIndexTemplateAction.INSTANCE, getComposableTemplatesRequest, getComposableTemplatesStep);
75+
76+
final ActionListener<Table> tableListener = new RestResponseListener<>(channel) {
77+
@Override
78+
public RestResponse buildResponse(Table table) throws Exception {
79+
return RestTable.buildResponse(table, channel);
80+
}
81+
};
82+
83+
getIndexTemplatesStep.whenComplete(getIndexTemplatesResponse ->
84+
getComposableTemplatesStep.whenComplete(getComposableIndexTemplatesResponse ->
85+
ActionListener.completeWith(tableListener, () -> buildTable(
86+
request,
87+
getIndexTemplatesResponse,
88+
getComposableIndexTemplatesResponse,
89+
templateNames)
90+
), tableListener::onFailure
91+
), tableListener::onFailure);
92+
};
6293
}
6394

6495
@Override
@@ -74,26 +105,30 @@ protected Table getTableWithHeader(RestRequest request) {
74105
return table;
75106
}
76107

77-
private Table buildTable(RestRequest request, ClusterStateResponse clusterStateResponse, String patternString) {
78-
Table table = getTableWithHeader(request);
79-
Metadata metadata = clusterStateResponse.getState().metadata();
80-
for (ObjectObjectCursor<String, IndexTemplateMetadata> entry : metadata.templates()) {
81-
IndexTemplateMetadata indexData = entry.value;
82-
if (patternString == null || Regex.simpleMatch(patternString, indexData.name())) {
83-
table.startRow();
84-
table.addCell(indexData.name());
85-
table.addCell("[" + String.join(", ", indexData.patterns()) + "]");
86-
table.addCell(indexData.getOrder());
87-
table.addCell(indexData.getVersion());
88-
table.addCell("");
89-
table.endRow();
90-
}
108+
private Table buildTable(
109+
RestRequest request,
110+
GetIndexTemplatesResponse getIndexTemplatesResponse,
111+
GetComposableIndexTemplateAction.Response getComposableIndexTemplatesResponse,
112+
String[] requestedNames
113+
) {
114+
final Predicate<String> namePredicate = getNamePredicate(requestedNames);
115+
116+
final Table table = getTableWithHeader(request);
117+
for (IndexTemplateMetadata indexData : getIndexTemplatesResponse.getIndexTemplates()) {
118+
assert namePredicate.test(indexData.getName());
119+
table.startRow();
120+
table.addCell(indexData.name());
121+
table.addCell("[" + String.join(", ", indexData.patterns()) + "]");
122+
table.addCell(indexData.getOrder());
123+
table.addCell(indexData.getVersion());
124+
table.addCell("");
125+
table.endRow();
91126
}
92127

93-
for (Map.Entry<String, ComposableIndexTemplate> entry : metadata.templatesV2().entrySet()) {
94-
String name = entry.getKey();
95-
ComposableIndexTemplate template = entry.getValue();
96-
if (patternString == null || Regex.simpleMatch(patternString, name)) {
128+
for (Map.Entry<String, ComposableIndexTemplate> entry : getComposableIndexTemplatesResponse.indexTemplates().entrySet()) {
129+
final String name = entry.getKey();
130+
if (namePredicate.test(name)) {
131+
final ComposableIndexTemplate template = entry.getValue();
97132
table.startRow();
98133
table.addCell(name);
99134
table.addCell("[" + String.join(", ", template.indexPatterns()) + "]");
@@ -103,6 +138,41 @@ private Table buildTable(RestRequest request, ClusterStateResponse clusterStateR
103138
table.endRow();
104139
}
105140
}
141+
106142
return table;
107143
}
144+
145+
private Predicate<String> getNamePredicate(String[] requestedNames) {
146+
if (requestedNames.length == 0) {
147+
return name -> true;
148+
}
149+
150+
final Set<String> exactMatches = new HashSet<>();
151+
final List<String> patterns = new ArrayList<>();
152+
for (String requestedName : requestedNames) {
153+
if (Regex.isMatchAllPattern(requestedName)) {
154+
return name -> true;
155+
} else if (Regex.isSimpleMatchPattern(requestedName)) {
156+
patterns.add(requestedName);
157+
} else {
158+
exactMatches.add(requestedName);
159+
}
160+
}
161+
162+
if (patterns.isEmpty()) {
163+
return exactMatches::contains;
164+
}
165+
166+
return name -> {
167+
if (exactMatches.contains(name)) {
168+
return true;
169+
}
170+
for (String pattern : patterns) {
171+
if (Regex.simpleMatch(pattern, name)) {
172+
return true;
173+
}
174+
}
175+
return false;
176+
};
177+
}
108178
}

0 commit comments

Comments
 (0)