Skip to content

Commit 8039973

Browse files
[8.5] Implement repair functionality for aliases colliding with indices bug (#91993)
* Fix corrupted Metadata from index and alias having the same name (#91456) This fixes a bug introduced in #87863 which added a Metadata copy constructor with separate name collision checks that assumed index name and alias names were already validated in `IndexMetada`. => fixed corrupted states by actually adding the validation to `IndexMetadata` to make it impossible to instantiate a broken `IndexMetadata` in the first place. * Implement repair functionality for aliases colliding with indices bug (#91887) Follow up to #91456 implementing an automated fix for indices corrupted in 8.5.
1 parent 2673202 commit 8039973

File tree

3 files changed

+63
-15
lines changed

3 files changed

+63
-15
lines changed

docs/changelog/91456.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 91456
2+
summary: Fix corrupted Metadata from index and alias having the same name
3+
area: Cluster Coordination
4+
type: bug
5+
issues: []

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

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
package org.elasticsearch.cluster.metadata;
1010

11+
import org.apache.logging.log4j.LogManager;
12+
import org.apache.logging.log4j.Logger;
1113
import org.elasticsearch.Version;
1214
import org.elasticsearch.action.admin.indices.rollover.RolloverInfo;
1315
import org.elasticsearch.action.support.ActiveShardCount;
@@ -82,6 +84,8 @@
8284

8385
public class IndexMetadata implements Diffable<IndexMetadata>, ToXContentFragment {
8486

87+
private static final Logger logger = LogManager.getLogger(IndexMetadata.class);
88+
8589
public static final ClusterBlock INDEX_READ_ONLY_BLOCK = new ClusterBlock(
8690
5,
8791
"index read-only (api)",
@@ -1512,7 +1516,7 @@ public IndexMetadata apply(IndexMetadata part) {
15121516
builder.rolloverInfos.putAllFromMap(rolloverInfos.apply(part.rolloverInfos));
15131517
builder.system(isSystem);
15141518
builder.timestampRange(timestampRange);
1515-
return builder.build();
1519+
return builder.build(true);
15161520
}
15171521
}
15181522

@@ -1573,7 +1577,7 @@ public static IndexMetadata readFrom(StreamInput in, @Nullable Function<String,
15731577
builder.system(in.readBoolean());
15741578
}
15751579
builder.timestampRange(IndexLongFieldRange.readFrom(in));
1576-
return builder.build();
1580+
return builder.build(true);
15771581
}
15781582

15791583
/**
@@ -1903,6 +1907,11 @@ public Builder timestampRange(IndexLongFieldRange timestampRange) {
19031907
}
19041908

19051909
public IndexMetadata build() {
1910+
return build(false);
1911+
}
1912+
1913+
// package private for testing
1914+
IndexMetadata build(boolean repair) {
19061915
/*
19071916
* We expect that the metadata has been properly built to set the number of shards and the number of replicas, and do not rely
19081917
* on the default values here. Those must have been set upstream.
@@ -2020,7 +2029,16 @@ public IndexMetadata build() {
20202029
var aliasesMap = aliases.build();
20212030
for (AliasMetadata alias : aliasesMap.values()) {
20222031
if (alias.alias().equals(index)) {
2023-
throw new IllegalArgumentException("alias name [" + index + "] self-conflicts with index name");
2032+
if (repair && indexCreatedVersion.equals(Version.V_8_5_0)) {
2033+
var updatedBuilder = ImmutableOpenMap.builder(aliasesMap);
2034+
final var brokenAlias = updatedBuilder.remove(index);
2035+
final var fixedAlias = AliasMetadata.newAliasMetadata(brokenAlias, index + "-alias-corrupted-by-8-5");
2036+
aliasesMap = updatedBuilder.fPut(fixedAlias.getAlias(), fixedAlias).build();
2037+
logger.warn("Repaired corrupted alias with the same name as its index for [{}]", index);
2038+
break;
2039+
} else {
2040+
throw new IllegalArgumentException("alias name [" + index + "] self-conflicts with index name");
2041+
}
20242042
}
20252043
}
20262044

@@ -2321,7 +2339,7 @@ public static IndexMetadata fromXContent(XContentParser parser, Map<String, Mapp
23212339
assert settingsVersion : "settings version should be present for indices created on or after 6.5.0";
23222340
assert indexCreatedVersion(builder.settings).before(Version.V_7_2_0) || aliasesVersion
23232341
: "aliases version should be present for indices created on or after 7.2.0";
2324-
return builder.build();
2342+
return builder.build(true);
23252343
}
23262344

23272345
/**
@@ -2433,7 +2451,7 @@ public static IndexMetadata legacyFromXContent(XContentParser parser) throws IOE
24332451
builder.putMapping(MappingMetadata.EMPTY_MAPPINGS); // just make sure it's not empty so that _source can be read
24342452
}
24352453

2436-
IndexMetadata indexMetadata = builder.build();
2454+
IndexMetadata indexMetadata = builder.build(true);
24372455
assert indexMetadata.getCreationVersion().isLegacyIndexVersion();
24382456
assert indexMetadata.getCompatibilityVersion().isLegacyIndexVersion();
24392457
return indexMetadata;

server/src/test/java/org/elasticsearch/cluster/metadata/IndexMetadataTests.java

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import static org.elasticsearch.cluster.metadata.IndexMetadata.parseIndexNameCounter;
4747
import static org.hamcrest.Matchers.containsString;
4848
import static org.hamcrest.Matchers.equalTo;
49+
import static org.hamcrest.Matchers.hasKey;
4950
import static org.hamcrest.Matchers.is;
5051

5152
public class IndexMetadataTests extends ESTestCase {
@@ -483,16 +484,40 @@ public void testLifeCyclePolicyName() {
483484
}
484485

485486
public void testIndexAndAliasWithSameName() {
486-
final IllegalArgumentException iae = expectThrows(
487-
IllegalArgumentException.class,
488-
() -> IndexMetadata.builder("index")
489-
.settings(Settings.builder().put(IndexMetadata.SETTING_VERSION_CREATED, Version.CURRENT))
490-
.numberOfShards(1)
491-
.numberOfReplicas(0)
492-
.putAlias(AliasMetadata.builder("index").build())
493-
.build()
494-
);
495-
assertEquals("alias name [index] self-conflicts with index name", iae.getMessage());
487+
{
488+
final IllegalArgumentException iae = expectThrows(
489+
IllegalArgumentException.class,
490+
() -> IndexMetadata.builder("index")
491+
.settings(Settings.builder().put(IndexMetadata.SETTING_VERSION_CREATED, Version.CURRENT))
492+
.numberOfShards(1)
493+
.numberOfReplicas(0)
494+
.putAlias(AliasMetadata.builder("index").build())
495+
.build(randomBoolean())
496+
);
497+
assertEquals("alias name [index] self-conflicts with index name", iae.getMessage());
498+
}
499+
{
500+
final IllegalArgumentException iae = expectThrows(
501+
IllegalArgumentException.class,
502+
() -> IndexMetadata.builder("index")
503+
.settings(Settings.builder().put(IndexMetadata.SETTING_VERSION_CREATED, Version.V_8_5_0))
504+
.numberOfShards(1)
505+
.numberOfReplicas(0)
506+
.putAlias(AliasMetadata.builder("index").build())
507+
.build(false)
508+
);
509+
assertEquals("alias name [index] self-conflicts with index name", iae.getMessage());
510+
}
511+
}
512+
513+
public void testRepairIndexAndAliasWithSameName() {
514+
final IndexMetadata indexMetadata = IndexMetadata.builder("index")
515+
.settings(Settings.builder().put(IndexMetadata.SETTING_VERSION_CREATED, Version.V_8_5_0))
516+
.numberOfShards(1)
517+
.numberOfReplicas(0)
518+
.putAlias(AliasMetadata.builder("index").build())
519+
.build(true);
520+
assertThat(indexMetadata.getAliases(), hasKey("index-alias-corrupted-by-8-5"));
496521
}
497522

498523
private static Settings indexSettingsWithDataTier(String dataTier) {

0 commit comments

Comments
 (0)