Skip to content

Commit c64900e

Browse files
authored
[8.12] Fix _alias/<alias> returning non-matching data streams (#104145) (#104880)
* Fix _alias/<alias> returning non-matching data streams (#104145) This fixes a bug where GET _alias/<alias> would return non-matching data streams * Remove problematic assertions * Change to `containsInAnyOrder` in assertions for lists
1 parent 688628b commit c64900e

File tree

9 files changed

+267
-33
lines changed

9 files changed

+267
-33
lines changed

docs/changelog/104145.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pr: 104145
2+
summary: Fix _alias/<alias> returning non-matching data streams
3+
area: Data streams
4+
type: bug
5+
issues:
6+
- 96589

modules/data-streams/src/yamlRestTest/resources/rest-api-spec/test/data_stream/140_data_stream_aliases.yml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,3 +260,50 @@
260260
- match: {test2.aliases: {}}
261261
- match: {test3.aliases: {}}
262262

263+
---
264+
"Test get alias with non-matching data streams":
265+
- skip:
266+
version: " - 8.12.1"
267+
reason: "bugfix fixed from 8.12.1 and later"
268+
features: allowed_warnings
269+
270+
- do:
271+
allowed_warnings:
272+
- "index template [my-template] has index patterns [ds-*] matching patterns from existing older templates [global] with patterns (global => [*]); this template [my-template] will take precedence during new index creation"
273+
indices.put_index_template:
274+
name: my-template
275+
body:
276+
index_patterns: [ ds-* ]
277+
template:
278+
settings:
279+
index.number_of_replicas: 0
280+
data_stream: { }
281+
282+
- do:
283+
indices.create_data_stream:
284+
name: ds-first
285+
- is_true: acknowledged
286+
287+
- do:
288+
indices.create_data_stream:
289+
name: ds-second
290+
- is_true: acknowledged
291+
292+
- do:
293+
indices.update_aliases:
294+
body:
295+
actions:
296+
- add:
297+
index: ds-first
298+
alias: my-alias
299+
- is_true: acknowledged
300+
301+
- do:
302+
indices.get_alias:
303+
name: my-al*
304+
- match: {ds-first.aliases.my-alias: {}}
305+
306+
- do:
307+
indices.get_alias:
308+
name: this-does-not-exist*
309+
- is_false: ds-first.aliases.my-alias

rest-api-spec/src/yamlRestTest/resources/rest-api-spec/test/cluster.component_template/10_basic.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@
118118
"Add data stream lifecycle":
119119
- skip:
120120
version: " - 8.10.99"
121-
reason: "Data stream lifecycle was GA in 8.11"
121+
reason: "Data stream lifecycle was available from 8.11"
122122

123123
- do:
124124
cluster.put_component_template:
@@ -146,7 +146,7 @@
146146
"Get data stream lifecycle with default rollover":
147147
- skip:
148148
version: " - 8.10.99"
149-
reason: "Data stream lifecycle was GA in 8.11"
149+
reason: "Data stream lifecycle was available from 8.11"
150150

151151
- do:
152152
cluster.put_component_template:

server/src/main/java/org/elasticsearch/action/admin/indices/alias/get/TransportGetAliasesAction.java

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
import org.elasticsearch.common.inject.Inject;
2323
import org.elasticsearch.common.logging.DeprecationCategory;
2424
import org.elasticsearch.common.logging.DeprecationLogger;
25-
import org.elasticsearch.common.regex.Regex;
2625
import org.elasticsearch.common.util.concurrent.ThreadContext;
2726
import org.elasticsearch.core.UpdateForV9;
2827
import org.elasticsearch.indices.SystemIndices;
@@ -147,21 +146,9 @@ static Map<String, List<DataStreamAlias>> postProcess(
147146
ClusterState state
148147
) {
149148
Map<String, List<DataStreamAlias>> result = new HashMap<>();
150-
boolean noAliasesSpecified = request.getOriginalAliases() == null || request.getOriginalAliases().length == 0;
151149
List<String> requestedDataStreams = resolver.dataStreamNames(state, request.indicesOptions(), request.indices());
152-
for (String requestedDataStream : requestedDataStreams) {
153-
List<DataStreamAlias> aliases = state.metadata()
154-
.dataStreamAliases()
155-
.values()
156-
.stream()
157-
.filter(alias -> alias.getDataStreams().contains(requestedDataStream))
158-
.filter(alias -> noAliasesSpecified || Regex.simpleMatch(request.aliases(), alias.getName()))
159-
.toList();
160-
if (aliases.isEmpty() == false) {
161-
result.put(requestedDataStream, aliases);
162-
}
163-
}
164-
return result;
150+
151+
return state.metadata().findDataStreamAliases(request.aliases(), requestedDataStreams.toArray(new String[0]));
165152
}
166153

167154
private static void checkSystemIndexAccess(
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0 and the Server Side Public License, v 1; you may not use this file except
5+
* in compliance with, at your election, the Elastic License 2.0 or the Server
6+
* Side Public License, v 1.
7+
*/
8+
9+
package org.elasticsearch.cluster.metadata;
10+
11+
/**
12+
* Used as a common interface for AliasMetadata and DataStreamAlias
13+
*/
14+
interface AliasInfo {
15+
String getAlias();
16+
}

server/src/main/java/org/elasticsearch/cluster/metadata/AliasMetadata.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232

3333
import static java.util.Collections.emptySet;
3434

35-
public class AliasMetadata implements SimpleDiffable<AliasMetadata>, ToXContentFragment {
35+
public class AliasMetadata implements SimpleDiffable<AliasMetadata>, ToXContentFragment, AliasInfo {
3636

3737
private final String alias;
3838

server/src/main/java/org/elasticsearch/cluster/metadata/DataStreamAlias.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
import java.util.function.Predicate;
3838
import java.util.stream.Collectors;
3939

40-
public class DataStreamAlias implements SimpleDiffable<DataStreamAlias>, ToXContentFragment {
40+
public class DataStreamAlias implements SimpleDiffable<DataStreamAlias>, ToXContentFragment, AliasInfo {
4141

4242
public static final ParseField DATA_STREAMS_FIELD = new ParseField("data_streams");
4343
public static final ParseField WRITE_DATA_STREAM_FIELD = new ParseField("write_data_stream");
@@ -191,6 +191,13 @@ public String getName() {
191191
return name;
192192
}
193193

194+
/**
195+
* Returns the alias name, which is the same value as getName()
196+
*/
197+
public String getAlias() {
198+
return getName();
199+
}
200+
194201
/**
195202
* Returns the data streams that are referenced
196203
*/

server/src/main/java/org/elasticsearch/cluster/metadata/Metadata.java

Lines changed: 96 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,16 @@ default boolean isRestorable() {
234234

235235
private final IndexVersion oldestIndexVersion;
236236

237+
// Used in the findAliases and findDataStreamAliases functions
238+
private interface AliasInfoGetter {
239+
List<? extends AliasInfo> get(String entityName);
240+
}
241+
242+
// Used in the findAliases and findDataStreamAliases functions
243+
private interface AliasInfoSetter {
244+
void put(String entityName, List<AliasInfo> aliases);
245+
}
246+
237247
private Metadata(
238248
String clusterUUID,
239249
boolean clusterUUIDCommitted,
@@ -799,11 +809,63 @@ public Map<String, List<AliasMetadata>> findAllAliases(final String[] concreteIn
799809
* aliases then the result will <b>not</b> include the index's key.
800810
*/
801811
public Map<String, List<AliasMetadata>> findAliases(final String[] aliases, final String[] concreteIndices) {
812+
ImmutableOpenMap.Builder<String, List<AliasMetadata>> mapBuilder = ImmutableOpenMap.builder();
813+
814+
AliasInfoGetter getter = index -> indices.get(index).getAliases().values().stream().toList();
815+
816+
AliasInfoSetter setter = (index, foundAliases) -> {
817+
List<AliasMetadata> d = new ArrayList<>();
818+
foundAliases.forEach(i -> d.add((AliasMetadata) i));
819+
mapBuilder.put(index, d);
820+
};
821+
822+
findAliasInfo(aliases, concreteIndices, getter, setter);
823+
824+
return mapBuilder.build();
825+
}
826+
827+
/**
828+
* Finds the specific data stream aliases that match with the specified aliases directly or partially via wildcards, and
829+
* that point to the specified data streams (directly or matching data streams via wildcards).
830+
*
831+
* @param aliases The aliases to look for. Might contain include or exclude wildcards.
832+
* @param dataStreams The data streams that the aliases must point to in order to be returned
833+
* @return A map of data stream name to the list of DataStreamAlias objects that match. If a data stream does not have matching
834+
* aliases then the result will <b>not</b> include the data stream's key.
835+
*/
836+
public Map<String, List<DataStreamAlias>> findDataStreamAliases(final String[] aliases, final String[] dataStreams) {
837+
ImmutableOpenMap.Builder<String, List<DataStreamAlias>> mapBuilder = ImmutableOpenMap.builder();
838+
Map<String, List<DataStreamAlias>> dataStreamAliases = dataStreamAliasesByDataStream();
839+
840+
AliasInfoGetter getter = dataStream -> dataStreamAliases.getOrDefault(dataStream, Collections.emptyList());
841+
842+
AliasInfoSetter setter = (dataStream, foundAliases) -> {
843+
List<DataStreamAlias> dsAliases = new ArrayList<>();
844+
foundAliases.forEach(alias -> dsAliases.add((DataStreamAlias) alias));
845+
mapBuilder.put(dataStream, dsAliases);
846+
};
847+
848+
findAliasInfo(aliases, dataStreams, getter, setter);
849+
850+
return mapBuilder.build();
851+
}
852+
853+
/**
854+
* Find the aliases that point to the specified data streams or indices. Called from findAliases or findDataStreamAliases.
855+
*
856+
* @param aliases The aliases to look for. Might contain include or exclude wildcards.
857+
* @param possibleMatches The data streams or indices that the aliases must point to in order to be returned
858+
* @param getter A function that is used to get the alises for a given data stream or index
859+
* @param setter A function that is used to keep track of the found aliases
860+
*/
861+
private void findAliasInfo(final String[] aliases, final String[] possibleMatches, AliasInfoGetter getter, AliasInfoSetter setter) {
802862
assert aliases != null;
803-
assert concreteIndices != null;
804-
if (concreteIndices.length == 0) {
805-
return ImmutableOpenMap.of();
863+
assert possibleMatches != null;
864+
if (possibleMatches.length == 0) {
865+
return;
806866
}
867+
868+
// create patterns to use to search for targets
807869
String[] patterns = new String[aliases.length];
808870
boolean[] include = new boolean[aliases.length];
809871
for (int i = 0; i < aliases.length; i++) {
@@ -816,14 +878,16 @@ public Map<String, List<AliasMetadata>> findAliases(final String[] aliases, fina
816878
include[i] = true;
817879
}
818880
}
881+
819882
boolean matchAllAliases = patterns.length == 0;
820-
ImmutableOpenMap.Builder<String, List<AliasMetadata>> mapBuilder = ImmutableOpenMap.builder();
821-
for (String index : concreteIndices) {
822-
IndexMetadata indexMetadata = indices.get(index);
823-
List<AliasMetadata> filteredValues = new ArrayList<>();
824-
for (AliasMetadata aliasMetadata : indexMetadata.getAliases().values()) {
883+
884+
for (String index : possibleMatches) {
885+
List<AliasInfo> filteredValues = new ArrayList<>();
886+
887+
List<? extends AliasInfo> entities = getter.get(index);
888+
for (AliasInfo aliasInfo : entities) {
825889
boolean matched = matchAllAliases;
826-
String alias = aliasMetadata.alias();
890+
String alias = aliasInfo.getAlias();
827891
for (int i = 0; i < patterns.length; i++) {
828892
if (include[i]) {
829893
if (matched == false) {
@@ -835,16 +899,15 @@ public Map<String, List<AliasMetadata>> findAliases(final String[] aliases, fina
835899
}
836900
}
837901
if (matched) {
838-
filteredValues.add(aliasMetadata);
902+
filteredValues.add(aliasInfo);
839903
}
840904
}
841905
if (filteredValues.isEmpty() == false) {
842906
// Make the list order deterministic
843-
CollectionUtil.timSort(filteredValues, Comparator.comparing(AliasMetadata::alias));
844-
mapBuilder.put(index, Collections.unmodifiableList(filteredValues));
907+
CollectionUtil.timSort(filteredValues, Comparator.comparing(AliasInfo::getAlias));
908+
setter.put(index, Collections.unmodifiableList(filteredValues));
845909
}
846910
}
847-
return mapBuilder.build();
848911
}
849912

850913
/**
@@ -1264,6 +1327,25 @@ public Map<String, DataStreamAlias> dataStreamAliases() {
12641327
return this.custom(DataStreamMetadata.TYPE, DataStreamMetadata.EMPTY).getDataStreamAliases();
12651328
}
12661329

1330+
/**
1331+
* Return a map of DataStreamAlias objects by DataStream name
1332+
* @return a map of DataStreamAlias objects by DataStream name
1333+
*/
1334+
public Map<String, List<DataStreamAlias>> dataStreamAliasesByDataStream() {
1335+
Map<String, List<DataStreamAlias>> dataStreamAliases = new HashMap<>();
1336+
1337+
for (DataStreamAlias dsAlias : dataStreamAliases().values()) {
1338+
for (String dataStream : dsAlias.getDataStreams()) {
1339+
if (dataStreamAliases.containsKey(dataStream) == false) {
1340+
dataStreamAliases.put(dataStream, new ArrayList<>());
1341+
}
1342+
dataStreamAliases.get(dataStream).add(dsAlias);
1343+
}
1344+
}
1345+
1346+
return dataStreamAliases;
1347+
}
1348+
12671349
public NodesShutdownMetadata nodeShutdowns() {
12681350
return custom(NodesShutdownMetadata.TYPE, NodesShutdownMetadata.EMPTY);
12691351
}
@@ -2432,7 +2514,7 @@ private static void collectAliasDuplicates(
24322514
reported = true;
24332515
}
24342516
}
2435-
// This is for adding an error message for when a data steam alias has the same name as a data stream.
2517+
// This is for adding an error message for when a data stream alias has the same name as a data stream.
24362518
if (reported == false && dataStreamMetadata != null && dataStreamMetadata.dataStreams().containsKey(alias)) {
24372519
duplicates.add("data stream alias and data stream have the same name (" + alias + ")");
24382520
}

0 commit comments

Comments
 (0)