Skip to content

Commit 59cc4ba

Browse files
authored
Adjust /_cat/templates not to request all metadata (#78819)
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 #78812
1 parent 1c1e732 commit 59cc4ba

File tree

2 files changed

+284
-35
lines changed

2 files changed

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