Skip to content

Commit 9949b6b

Browse files
authored
Adjust /_cat/templates not to request all metadata (#78832)
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. Backport of #78829
1 parent 2e8ac06 commit 9949b6b

File tree

2 files changed

+293
-35
lines changed

2 files changed

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

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

6697
@Override
@@ -76,26 +107,30 @@ protected Table getTableWithHeader(RestRequest request) {
76107
return table;
77108
}
78109

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

95-
for (Map.Entry<String, ComposableIndexTemplate> entry : metadata.templatesV2().entrySet()) {
96-
String name = entry.getKey();
97-
ComposableIndexTemplate template = entry.getValue();
98-
if (patternString == null || Regex.simpleMatch(patternString, name)) {
130+
for (Map.Entry<String, ComposableIndexTemplate> entry : getComposableIndexTemplatesResponse.indexTemplates().entrySet()) {
131+
final String name = entry.getKey();
132+
if (namePredicate.test(name)) {
133+
final ComposableIndexTemplate template = entry.getValue();
99134
table.startRow();
100135
table.addCell(name);
101136
table.addCell("[" + String.join(", ", template.indexPatterns()) + "]");
@@ -105,6 +140,41 @@ private Table buildTable(RestRequest request, ClusterStateResponse clusterStateR
105140
table.endRow();
106141
}
107142
}
143+
108144
return table;
109145
}
146+
147+
private Predicate<String> getNamePredicate(String[] requestedNames) {
148+
if (requestedNames.length == 0) {
149+
return name -> true;
150+
}
151+
152+
final Set<String> exactMatches = new HashSet<>();
153+
final List<String> patterns = new ArrayList<>();
154+
for (String requestedName : requestedNames) {
155+
if (Regex.isMatchAllPattern(requestedName)) {
156+
return name -> true;
157+
} else if (Regex.isSimpleMatchPattern(requestedName)) {
158+
patterns.add(requestedName);
159+
} else {
160+
exactMatches.add(requestedName);
161+
}
162+
}
163+
164+
if (patterns.isEmpty()) {
165+
return exactMatches::contains;
166+
}
167+
168+
return name -> {
169+
if (exactMatches.contains(name)) {
170+
return true;
171+
}
172+
for (String pattern : patterns) {
173+
if (Regex.simpleMatch(pattern, name)) {
174+
return true;
175+
}
176+
}
177+
return false;
178+
};
179+
}
110180
}

0 commit comments

Comments
 (0)