+ * This method provides a reverse lookup from a blob UUID to its corresponding index metadata identifier.
+ * If the specified blob UUID is not present, this method returns {@code null}.
+ *
+ *
+ * @param blobUuid the UUID of the blob whose index metadata identifier is to be retrieved
+ * @return the index metadata identifier associated with the given blob UUID, or {@code null} if not found
+ */
+ @Nullable
+ public String getIndexMetadataIdentifierByBlobUuid(String blobUuid) {
+ return blobUuidToIndexMetadataMap.get(blobUuid);
+ }
+
/**
* Get the blob id by {@link SnapshotId} and {@link IndexId} and fall back to the value of {@link SnapshotId#getUUID()} if none is
* known to enable backwards compatibility with versions older than
@@ -156,7 +180,7 @@ public IndexMetaDataGenerations withRemovedSnapshots(Collection snap
@Override
public int hashCode() {
- return Objects.hash(identifiers, lookup);
+ return Objects.hash(identifiers, lookup, blobUuidToIndexMetadataMap);
}
@Override
@@ -168,12 +192,12 @@ public boolean equals(Object that) {
return false;
}
final IndexMetaDataGenerations other = (IndexMetaDataGenerations) that;
- return lookup.equals(other.lookup) && identifiers.equals(other.identifiers);
+ return lookup.equals(other.lookup) && identifiers.equals(other.identifiers) && blobUuidToIndexMetadataMap.equals(other.blobUuidToIndexMetadataMap);
}
@Override
public String toString() {
- return "IndexMetaDataGenerations{lookup:" + lookup + "}{identifier:" + identifiers + "}";
+ return "IndexMetaDataGenerations{lookup:" + lookup + "}{identifier:" + identifiers + "}{blobUuidToIndexMetadataMap:" + blobUuidToIndexMetadataMap + "}";
}
/**
@@ -199,31 +223,27 @@ public static String buildUniqueIdentifier(IndexMetadata indexMetaData) {
/**
* Given a blobId, returns the index UUID associated with it.
+ *
* If the blobId is not found, returns null.
+ *
+ * If the index UUID is _na_ {@code ClusterState.UNKNOWN_UUID}
* @param blobId The blob ID
*/
@Nullable
public String convertBlobIdToIndexUUID(String blobId) {
// Find the unique identifier for this blobId
- String uniqueIdentifier = null;
- for (Map.Entry entry : this.identifiers.entrySet()) {
- if (Objects.equals(entry.getValue(), blobId)) {
- uniqueIdentifier = entry.getKey();
- break;
- }
- }
+ String uniqueIdentifier = blobUuidToIndexMetadataMap.get(blobId);
if (uniqueIdentifier == null) {
return null;
}
- // The uniqueIdentifier was built in buildUniqueIdentifier, is of the format IndexUUID-String-long-long-long,
- // and uses '-' as a delimiter.
- // The below regex accounts for the fact that the IndexUUID can also contain the '-' character
- Pattern pattern = Pattern.compile("^(.*?)-[^-]+-\\d+-\\d+-\\d+$");
- Matcher matcher = pattern.matcher(uniqueIdentifier);
- if (matcher.matches()) {
- return matcher.group(1);
+ // The uniqueIdentifier is built in {@code buildUniqueIdentifier}, and is prefixed with indexUUID
+ // The indexUUID is either a random UUID of length 22, or _na_
+ boolean na = uniqueIdentifier.startsWith(ClusterState.UNKNOWN_UUID + "-");
+ if (na) {
+ return ClusterState.UNKNOWN_UUID;
}
- throw new IllegalArgumentException("Error parsing the IndexMetadata identifier");
+ assert uniqueIdentifier.length() >=22;
+ return uniqueIdentifier.substring(0, 22);
}
}
diff --git a/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java b/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java
index f7f41e3c5371c..abb02d4fcaf81 100644
--- a/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java
+++ b/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java
@@ -1262,11 +1262,7 @@ private class IndexSnapshotsDeletion {
private final IndexId indexId;
private final Set snapshotsWithIndex;
private final BlobContainer indexContainer;
-
- /**
- * Maps the Index UUID to its shard count
- */
- private final ConcurrentMap indexUUIDToShardCountMap = new ConcurrentHashMap<>();
+ private final Set indexUUIDs = new HashSet<>();
IndexSnapshotsDeletion(IndexId indexId) {
this.indexId = indexId;
@@ -1294,12 +1290,17 @@ private void determineShardCount(ActionListener listener) {
.filter(snapshotsWithIndex::contains)
.map(id -> originalRepositoryData.indexMetaDataGenerations().indexMetaBlobId(id, indexId))
.collect(Collectors.toSet())) {
- snapshotExecutor.execute(ActionRunnable.run(listeners.acquire(), () -> {
- // NB since 7.9.0 we deduplicate index metadata blobs, and one of the components of the deduplication key is the
- // index UUID; the shard count is going to be the same for all metadata with the same index UUID, so it is
- // unnecessary to read multiple metadata blobs corresponding to the same index UUID.
- String indexUUID = originalRepositoryData.indexMetaDataGenerations().convertBlobIdToIndexUUID(blobId);
- if (indexUUIDToShardCountMap.containsKey(indexUUID) == false) {
+ // NB since 7.9.0 we deduplicate index metadata blobs, and one of the components of the deduplication key is the
+ // index UUID; the shard count is going to be the same for all metadata with the same index UUID, so it is
+ // unnecessary to read multiple metadata blobs corresponding to the same index UUID.
+ // NB if the index metadata blob is in the pre-7.9.0 format then this will return null
+ String indexUUID = originalRepositoryData.indexMetaDataGenerations().convertBlobIdToIndexUUID(blobId);
+
+ // Without an index UUID, we don't know if we've encountered this index before and must read it's IndexMetadata
+ // from heap. If this is a new index UUID, with a possibly higher shard count, then we also need to read
+ // it's IndexMetadata from heap
+ if (indexUUID == null || indexUUIDs.add(indexUUID)) {
+ snapshotExecutor.execute(ActionRunnable.run(listeners.acquire(), () -> {
try {
IndexMetadata indexMetadata = INDEX_METADATA_FORMAT.read(
getProjectRepo(),
@@ -1307,9 +1308,7 @@ private void determineShardCount(ActionListener listener) {
blobId,
namedXContentRegistry
);
- int numberOfShards = indexMetadata.getNumberOfShards();
- indexUUIDToShardCountMap.put(indexUUID, numberOfShards);
- updateShardCount(numberOfShards);
+ updateShardCount(indexMetadata.getNumberOfShards());
} catch (Exception ex) {
logger.warn(() -> format("[%s] [%s] failed to read metadata for index", blobId, indexId.getName()), ex);
// Definitely indicates something fairly badly wrong with the repo, but not immediately fatal here: we
@@ -1321,12 +1320,8 @@ private void determineShardCount(ActionListener listener) {
// extra data anyway.
// TODO: Should we fail the delete here? See https://github.com/elastic/elasticsearch/issues/100569.
}
- } else {
- // indexUUIDToShardCountMap is shared across all threads. Therefore, while there may be an entry for this
- // UUID in the map, there is no guarantee that we've encountered it in this thread.
- updateShardCount(indexUUIDToShardCountMap.get(indexUUID));
- }
- }));
+ }));
+ }
}
}
}
diff --git a/server/src/test/java/org/elasticsearch/repositories/IndexMetaDataGenerationsTests.java b/server/src/test/java/org/elasticsearch/repositories/IndexMetaDataGenerationsTests.java
index 9ade115ab6029..ff4154e3f21cc 100644
--- a/server/src/test/java/org/elasticsearch/repositories/IndexMetaDataGenerationsTests.java
+++ b/server/src/test/java/org/elasticsearch/repositories/IndexMetaDataGenerationsTests.java
@@ -9,11 +9,14 @@
package org.elasticsearch.repositories;
+import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.IndexMetadata;
+import org.elasticsearch.common.UUIDs;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.snapshots.SnapshotId;
import org.elasticsearch.test.ESTestCase;
+import java.util.HashMap;
import java.util.Map;
import static org.mockito.Mockito.mock;
@@ -21,44 +24,62 @@
public class IndexMetaDataGenerationsTests extends ESTestCase {
+ public void testIndexMetaDataGenerations() {
+ Map identifiers = new HashMap<>();
+ Map lookupInternal = new HashMap<>();
+ Map blobUuidToIndexMetadataMap = new HashMap<>();
+
+ int numberOfMetadataIdentifiers = randomIntBetween(5, 10);
+ for (int i = 0; i < numberOfMetadataIdentifiers; i++) {
+ String indexUUID = generateUUID();
+ String metaIdentifier = generateMetaIdentifier(indexUUID);
+ String blobUUID = randomAlphanumericOfLength(randomIntBetween(5, 10));
+ identifiers.put(metaIdentifier, blobUUID);
+ blobUuidToIndexMetadataMap.put(blobUUID, metaIdentifier);
+
+ IndexId indexId = new IndexId(randomAlphanumericOfLength(10), indexUUID);
+ lookupInternal.put(indexId, metaIdentifier);
+ }
+
+ SnapshotId snapshotId = new SnapshotId(randomAlphanumericOfLength(10), randomUUID());
+ Map> lookup = Map.of(
+ snapshotId, lookupInternal
+ );
+
+ IndexMetaDataGenerations generations = new IndexMetaDataGenerations(lookup, identifiers);
+
+ assertEquals(lookup, generations.lookup);
+ assertEquals(identifiers, generations.identifiers);
+ assertEquals(blobUuidToIndexMetadataMap, generations.blobUuidToIndexMetadataMap);
+ }
+
public void testBuildUniqueIdentifierWithAllFieldsPresent() {
- String indexUUID = randomAlphanumericOfLength(randomIntBetween(10, 64));
+ String indexUUID = generateUUID();
String historyUUID = randomAlphanumericOfLength(randomIntBetween(10, 64));
long settingsVersion = randomLong();
long mappingVersion = randomLong();
long aliasesVersion = randomLong();
- IndexMetadata indexMetadata = mock(IndexMetadata.class);
- when(indexMetadata.getIndexUUID()).thenReturn(indexUUID);
- when(indexMetadata.getSettings()).thenReturn(Settings.builder().put(IndexMetadata.SETTING_HISTORY_UUID, historyUUID).build());
- when(indexMetadata.getSettingsVersion()).thenReturn(settingsVersion);
- when(indexMetadata.getMappingVersion()).thenReturn(mappingVersion);
- when(indexMetadata.getAliasesVersion()).thenReturn(aliasesVersion);
+ IndexMetadata indexMetadata = createIndexMetadata(indexUUID, historyUUID, settingsVersion, mappingVersion, aliasesVersion);
String result = IndexMetaDataGenerations.buildUniqueIdentifier(indexMetadata);
assertEquals(indexUUID + "-" + historyUUID + "-" + settingsVersion + "-" + mappingVersion + "-" + aliasesVersion, result);
}
public void testBuildUniqueIdentifierWithMissingHistoryUUID() {
- String indexUUID = randomAlphanumericOfLength(randomIntBetween(10, 64));
+ String indexUUID = generateUUID();
long settingsVersion = randomLong();
long mappingVersion = randomLong();
long aliasesVersion = randomLong();
- IndexMetadata indexMetadata = mock(IndexMetadata.class);
- when(indexMetadata.getIndexUUID()).thenReturn(indexUUID);
- when(indexMetadata.getSettings()).thenReturn(Settings.builder().build());
- when(indexMetadata.getSettingsVersion()).thenReturn(settingsVersion);
- when(indexMetadata.getMappingVersion()).thenReturn(mappingVersion);
- when(indexMetadata.getAliasesVersion()).thenReturn(aliasesVersion);
+ IndexMetadata indexMetadata = createIndexMetadata(indexUUID, null, settingsVersion, mappingVersion, aliasesVersion);
String result = IndexMetaDataGenerations.buildUniqueIdentifier(indexMetadata);
assertEquals(indexUUID + "-_na_-" + settingsVersion + "-" + mappingVersion + "-" + aliasesVersion, result);
}
public void testConvertBlobIdToIndexUUIDReturnsIndexUUID() {
- // May include the - symbol, which as of 9.3.0 is the same symbol as the delimiter
- String indexUUID = randomUUID();
+ String indexUUID = generateUUID();
String randomSetting = randomAlphaOfLength(randomIntBetween(5, 10));
long settingsVersion = randomNonNegativeLong();
long mappingsVersion = randomNonNegativeLong();
@@ -80,69 +101,29 @@ public void testConvertBlobIdToIndexUUIDReturnsNullWhenBlobIdIsNotFound() {
assertNull(generations.convertBlobIdToIndexUUID(randomAlphanumericOfLength(randomIntBetween(5, 10))));
}
- /**
- * A helper function that tests whether an IAE is thrown when a malformed IndexMetadata Identifier is passed into
- * the {@code convertBlobIdToIndexUUID} function
- * @param indexUUID The indexUUID
- * @param uniqueIdentifier The malformed identifier
- * @param blobId The blobId
- */
- private void internalMalformedIndexMetadataIdentifierThrowsIAE(String indexUUID, String uniqueIdentifier, String blobId) {
- // Creates the lookup map
- SnapshotId snapshotId = new SnapshotId("snapshot", randomUUID());
- IndexId indexId = new IndexId("index", indexUUID);
- Map> lookup = Map.of(snapshotId, Map.of(indexId, uniqueIdentifier));
-
- IndexMetaDataGenerations malformedGenerations = new IndexMetaDataGenerations(lookup, Map.of(uniqueIdentifier, blobId));
- IllegalArgumentException ex = assertThrows(
- IllegalArgumentException.class,
- () -> malformedGenerations.convertBlobIdToIndexUUID(blobId)
- );
- assertTrue(ex.getMessage().contains("Error parsing the IndexMetadata identifier"));
+ private String generateUUID() {
+ return usually() ? UUIDs.randomBase64UUID(random()) : ClusterState.UNKNOWN_UUID;
}
- public void testConvertBlobIdToIndexUUIDThrowsIllegalArgumentExceptionWhenMalformedIndexMetadataIdentifierIsMissingIndexUUID() {
- String indexUUID = randomUUID();
- String randomSetting = randomAlphaOfLength(randomIntBetween(5, 10));
- long settingsVersion = randomNonNegativeLong();
- long mappingsVersion = randomNonNegativeLong();
- long aliasesVersion = randomNonNegativeLong();
- // Build the unique identifier without including the index uuid
- String uniqueIdentifier = randomSetting + "-" + settingsVersion + "-" + mappingsVersion + "-" + aliasesVersion;
- String blobId = randomAlphanumericOfLength(randomIntBetween(5, 10));
- internalMalformedIndexMetadataIdentifierThrowsIAE(indexUUID, uniqueIdentifier, blobId);
- }
-
- public void testConvertBlobIdToIndexUUIDThrowsIllegalArgumentExceptionWhenMalformedIndexMetadataIdentifierIsMissingSettings() {
- String indexUUID = randomAlphanumericOfLength(randomIntBetween(5, 10));
- long settingsVersion = randomNonNegativeLong();
- long mappingsVersion = randomNonNegativeLong();
- long aliasesVersion = randomNonNegativeLong();
- // Build the unique identifier without including the settings
- String uniqueIdentifier = indexUUID + "-" + settingsVersion + "-" + mappingsVersion + "-" + aliasesVersion;
- String blobId = randomAlphanumericOfLength(randomIntBetween(5, 10));
- internalMalformedIndexMetadataIdentifierThrowsIAE(indexUUID, uniqueIdentifier, blobId);
- }
-
- public void testConvertBlobIdToIndexUUIDThrowsIllegalArgumentExceptionWhenMalformedIndexMetadataIdentifierIsMissingSettingsVersion() {
- String indexUUID = randomUUID();
- String randomSetting = randomAlphaOfLength(randomIntBetween(5, 10));
- long mappingsVersion = randomNonNegativeLong();
- long aliasesVersion = randomNonNegativeLong();
- // Build the unique identifier without including the settings version
- String uniqueIdentifier = indexUUID + "-" + randomSetting + "-" + mappingsVersion + "-" + aliasesVersion;
- String blobId = randomAlphanumericOfLength(randomIntBetween(5, 10));
- internalMalformedIndexMetadataIdentifierThrowsIAE(indexUUID, uniqueIdentifier, blobId);
+ private String generateMetaIdentifier(String indexUUID) {
+ String historyUUID = generateUUID();
+ long settingsVersion = randomLong();
+ long mappingVersion = randomLong();
+ long aliasesVersion = randomLong();
+ return indexUUID + "-" + historyUUID + "-" + settingsVersion + "-" + mappingVersion + "-" + aliasesVersion;
}
- public void testConvertBlobIdToIndexUUIDThrowsIllegalArgumentExceptionWhenMalformedIndexMetadataIdentifierHasIncorrectTypes() {
- String indexUUID = randomUUID();
- String randomSetting = randomAlphaOfLength(randomIntBetween(5, 10));
- long settingsVersion = randomNonNegativeLong();
- long mappingsVersion = randomNonNegativeLong();
- String aliasesVersion = randomAlphaOfLength(randomIntBetween(5, 10));
- String uniqueIdentifier = indexUUID + "-" + randomSetting + "-" + settingsVersion + "-" + mappingsVersion + "-" + aliasesVersion;
- String blobId = randomAlphanumericOfLength(randomIntBetween(5, 10));
- internalMalformedIndexMetadataIdentifierThrowsIAE(indexUUID, uniqueIdentifier, blobId);
+ private IndexMetadata createIndexMetadata(String indexUUID, String historyUUID, long settingsVersion, long mappingVersion, long aliasesVersion) {
+ IndexMetadata indexMetadata = mock(IndexMetadata.class);
+ Settings.Builder settingsBuilder = Settings.builder();
+ if (historyUUID != null) {
+ settingsBuilder.put(IndexMetadata.SETTING_HISTORY_UUID, historyUUID);
+ }
+ when(indexMetadata.getIndexUUID()).thenReturn(indexUUID);
+ when(indexMetadata.getSettings()).thenReturn(settingsBuilder.build());
+ when(indexMetadata.getSettingsVersion()).thenReturn(settingsVersion);
+ when(indexMetadata.getMappingVersion()).thenReturn(mappingVersion);
+ when(indexMetadata.getAliasesVersion()).thenReturn(aliasesVersion);
+ return indexMetadata;
}
}
From ab17c35bfed39ff8f49495a2cd4142693e478378 Mon Sep 17 00:00:00 2001
From: Joshua Adams
Date: Thu, 23 Oct 2025 11:22:51 +0100
Subject: [PATCH 10/17] Clean up
---
.../repositories/IndexMetaDataGenerations.java | 16 +++++++++++-----
.../blobstore/BlobStoreRepository.java | 1 -
.../IndexMetaDataGenerationsTests.java | 12 ++++++++----
3 files changed, 19 insertions(+), 10 deletions(-)
diff --git a/server/src/main/java/org/elasticsearch/repositories/IndexMetaDataGenerations.java b/server/src/main/java/org/elasticsearch/repositories/IndexMetaDataGenerations.java
index 820d0e624a3a8..49f134fdcc5c8 100644
--- a/server/src/main/java/org/elasticsearch/repositories/IndexMetaDataGenerations.java
+++ b/server/src/main/java/org/elasticsearch/repositories/IndexMetaDataGenerations.java
@@ -22,8 +22,6 @@
import java.util.Map;
import java.util.Objects;
import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
@@ -192,12 +190,20 @@ public boolean equals(Object that) {
return false;
}
final IndexMetaDataGenerations other = (IndexMetaDataGenerations) that;
- return lookup.equals(other.lookup) && identifiers.equals(other.identifiers) && blobUuidToIndexMetadataMap.equals(other.blobUuidToIndexMetadataMap);
+ return lookup.equals(other.lookup)
+ && identifiers.equals(other.identifiers)
+ && blobUuidToIndexMetadataMap.equals(other.blobUuidToIndexMetadataMap);
}
@Override
public String toString() {
- return "IndexMetaDataGenerations{lookup:" + lookup + "}{identifier:" + identifiers + "}{blobUuidToIndexMetadataMap:" + blobUuidToIndexMetadataMap + "}";
+ return "IndexMetaDataGenerations{lookup:"
+ + lookup
+ + "}{identifier:"
+ + identifiers
+ + "}{blobUuidToIndexMetadataMap:"
+ + blobUuidToIndexMetadataMap
+ + "}";
}
/**
@@ -243,7 +249,7 @@ public String convertBlobIdToIndexUUID(String blobId) {
if (na) {
return ClusterState.UNKNOWN_UUID;
}
- assert uniqueIdentifier.length() >=22;
+ assert uniqueIdentifier.length() >= 22;
return uniqueIdentifier.substring(0, 22);
}
}
diff --git a/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java b/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java
index abb02d4fcaf81..0b8e24ac509e2 100644
--- a/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java
+++ b/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java
@@ -164,7 +164,6 @@
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
diff --git a/server/src/test/java/org/elasticsearch/repositories/IndexMetaDataGenerationsTests.java b/server/src/test/java/org/elasticsearch/repositories/IndexMetaDataGenerationsTests.java
index ff4154e3f21cc..1331ea7bb858c 100644
--- a/server/src/test/java/org/elasticsearch/repositories/IndexMetaDataGenerationsTests.java
+++ b/server/src/test/java/org/elasticsearch/repositories/IndexMetaDataGenerationsTests.java
@@ -42,9 +42,7 @@ public void testIndexMetaDataGenerations() {
}
SnapshotId snapshotId = new SnapshotId(randomAlphanumericOfLength(10), randomUUID());
- Map> lookup = Map.of(
- snapshotId, lookupInternal
- );
+ Map> lookup = Map.of(snapshotId, lookupInternal);
IndexMetaDataGenerations generations = new IndexMetaDataGenerations(lookup, identifiers);
@@ -113,7 +111,13 @@ private String generateMetaIdentifier(String indexUUID) {
return indexUUID + "-" + historyUUID + "-" + settingsVersion + "-" + mappingVersion + "-" + aliasesVersion;
}
- private IndexMetadata createIndexMetadata(String indexUUID, String historyUUID, long settingsVersion, long mappingVersion, long aliasesVersion) {
+ private IndexMetadata createIndexMetadata(
+ String indexUUID,
+ String historyUUID,
+ long settingsVersion,
+ long mappingVersion,
+ long aliasesVersion
+ ) {
IndexMetadata indexMetadata = mock(IndexMetadata.class);
Settings.Builder settingsBuilder = Settings.builder();
if (historyUUID != null) {
From 10f710df0d96168c12fadd06bc2193f1c01507ac Mon Sep 17 00:00:00 2001
From: Joshua Adams
Date: Thu, 23 Oct 2025 11:30:51 +0100
Subject: [PATCH 11/17] Further clea up:
- Remove unreferenced method
- Rename convertBlobIdToIndexUUID to getIndexUUIDFromBlobId
- Tweak comments
---
.../IndexMetaDataGenerations.java | 21 +++----------------
.../blobstore/BlobStoreRepository.java | 2 +-
.../IndexMetaDataGenerationsTests.java | 4 ++--
...ryShardCountComputedOncePerIndexTests.java | 4 ++--
4 files changed, 8 insertions(+), 23 deletions(-)
diff --git a/server/src/main/java/org/elasticsearch/repositories/IndexMetaDataGenerations.java b/server/src/main/java/org/elasticsearch/repositories/IndexMetaDataGenerations.java
index 49f134fdcc5c8..7ffe3ff3bf8e6 100644
--- a/server/src/main/java/org/elasticsearch/repositories/IndexMetaDataGenerations.java
+++ b/server/src/main/java/org/elasticsearch/repositories/IndexMetaDataGenerations.java
@@ -78,21 +78,6 @@ public String getIndexMetaBlobId(String metaIdentifier) {
return identifiers.get(metaIdentifier);
}
- /**
- * Returns the index metadata identifier associated with the given blob UUID.
- *
- * This method provides a reverse lookup from a blob UUID to its corresponding index metadata identifier.
- * If the specified blob UUID is not present, this method returns {@code null}.
- *
- *
- * @param blobUuid the UUID of the blob whose index metadata identifier is to be retrieved
- * @return the index metadata identifier associated with the given blob UUID, or {@code null} if not found
- */
- @Nullable
- public String getIndexMetadataIdentifierByBlobUuid(String blobUuid) {
- return blobUuidToIndexMetadataMap.get(blobUuid);
- }
-
/**
* Get the blob id by {@link SnapshotId} and {@link IndexId} and fall back to the value of {@link SnapshotId#getUUID()} if none is
* known to enable backwards compatibility with versions older than
@@ -215,7 +200,7 @@ public String toString() {
* @return identifier string
*/
public static String buildUniqueIdentifier(IndexMetadata indexMetaData) {
- // If modifying this identifier, then also extend the convertBlobIdToIndexUUID function below
+ // If modifying this identifier, then also extend the getIndexUUIDFromBlobId function below
return indexMetaData.getIndexUUID()
+ "-"
+ indexMetaData.getSettings().get(IndexMetadata.SETTING_HISTORY_UUID, IndexMetadata.INDEX_UUID_NA_VALUE)
@@ -236,7 +221,7 @@ public static String buildUniqueIdentifier(IndexMetadata indexMetaData) {
* @param blobId The blob ID
*/
@Nullable
- public String convertBlobIdToIndexUUID(String blobId) {
+ public String getIndexUUIDFromBlobId(String blobId) {
// Find the unique identifier for this blobId
String uniqueIdentifier = blobUuidToIndexMetadataMap.get(blobId);
if (uniqueIdentifier == null) {
@@ -244,7 +229,7 @@ public String convertBlobIdToIndexUUID(String blobId) {
}
// The uniqueIdentifier is built in {@code buildUniqueIdentifier}, and is prefixed with indexUUID
- // The indexUUID is either a random UUID of length 22, or _na_
+ // The indexUUID is either a UUID of length 22, or _na_
boolean na = uniqueIdentifier.startsWith(ClusterState.UNKNOWN_UUID + "-");
if (na) {
return ClusterState.UNKNOWN_UUID;
diff --git a/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java b/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java
index 0b8e24ac509e2..ef59f66a4c630 100644
--- a/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java
+++ b/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java
@@ -1293,7 +1293,7 @@ private void determineShardCount(ActionListener listener) {
// index UUID; the shard count is going to be the same for all metadata with the same index UUID, so it is
// unnecessary to read multiple metadata blobs corresponding to the same index UUID.
// NB if the index metadata blob is in the pre-7.9.0 format then this will return null
- String indexUUID = originalRepositoryData.indexMetaDataGenerations().convertBlobIdToIndexUUID(blobId);
+ String indexUUID = originalRepositoryData.indexMetaDataGenerations().getIndexUUIDFromBlobId(blobId);
// Without an index UUID, we don't know if we've encountered this index before and must read it's IndexMetadata
// from heap. If this is a new index UUID, with a possibly higher shard count, then we also need to read
diff --git a/server/src/test/java/org/elasticsearch/repositories/IndexMetaDataGenerationsTests.java b/server/src/test/java/org/elasticsearch/repositories/IndexMetaDataGenerationsTests.java
index 1331ea7bb858c..f1325e0d3161b 100644
--- a/server/src/test/java/org/elasticsearch/repositories/IndexMetaDataGenerationsTests.java
+++ b/server/src/test/java/org/elasticsearch/repositories/IndexMetaDataGenerationsTests.java
@@ -91,12 +91,12 @@ public void testConvertBlobIdToIndexUUIDReturnsIndexUUID() {
Map> lookup = Map.of(snapshotId, Map.of(indexId, uniqueIdentifier));
IndexMetaDataGenerations generations = new IndexMetaDataGenerations(lookup, Map.of(uniqueIdentifier, blobId));
- assertEquals(indexUUID, generations.convertBlobIdToIndexUUID(blobId));
+ assertEquals(indexUUID, generations.getIndexUUIDFromBlobId(blobId));
}
public void testConvertBlobIdToIndexUUIDReturnsNullWhenBlobIdIsNotFound() {
IndexMetaDataGenerations generations = new IndexMetaDataGenerations(Map.of(), Map.of());
- assertNull(generations.convertBlobIdToIndexUUID(randomAlphanumericOfLength(randomIntBetween(5, 10))));
+ assertNull(generations.getIndexUUIDFromBlobId(randomAlphanumericOfLength(randomIntBetween(5, 10))));
}
private String generateUUID() {
diff --git a/server/src/test/java/org/elasticsearch/repositories/blobstore/BlobStoreRepositoryShardCountComputedOncePerIndexTests.java b/server/src/test/java/org/elasticsearch/repositories/blobstore/BlobStoreRepositoryShardCountComputedOncePerIndexTests.java
index 12f8fb781234c..8d234672a71d7 100644
--- a/server/src/test/java/org/elasticsearch/repositories/blobstore/BlobStoreRepositoryShardCountComputedOncePerIndexTests.java
+++ b/server/src/test/java/org/elasticsearch/repositories/blobstore/BlobStoreRepositoryShardCountComputedOncePerIndexTests.java
@@ -138,7 +138,7 @@ public InputStream readBlob(OperationPurpose purpose, String blobName) throws IO
- Generates A indices
- Generates M snapshots including these indices
- Deletes a subset B of indices
- - Recreates the B indices with the same name
+ - Recreates the B indices with the same name but a different shard count
- Generates N subsequent snapshots
- Deletes a random subset of snapshots within one request
@@ -168,7 +168,7 @@ public void testShardCountComputedOncePerIndexWhenDeletingMultipleSnapshotsConcu
- Generates A indices
- Generates M snapshots including these indices
- Deletes a subset B of indices
- - Recreates the B indices with the same name
+ - Recreates the B indices with the same name but a different shard count
- Generates N subsequent snapshots
- Deletes a random subset of snapshots within one request
From 31de1cb2fcabf02c70b538e84de2b61b7ef19f3d Mon Sep 17 00:00:00 2001
From: Joshua Adams
Date: Thu, 23 Oct 2025 11:50:09 +0100
Subject: [PATCH 12/17] Refactor Tests
---
...ryShardCountComputedOncePerIndexTests.java | 51 +++++++++----------
1 file changed, 24 insertions(+), 27 deletions(-)
diff --git a/server/src/test/java/org/elasticsearch/repositories/blobstore/BlobStoreRepositoryShardCountComputedOncePerIndexTests.java b/server/src/test/java/org/elasticsearch/repositories/blobstore/BlobStoreRepositoryShardCountComputedOncePerIndexTests.java
index 8d234672a71d7..bb15c9bb08c93 100644
--- a/server/src/test/java/org/elasticsearch/repositories/blobstore/BlobStoreRepositoryShardCountComputedOncePerIndexTests.java
+++ b/server/src/test/java/org/elasticsearch/repositories/blobstore/BlobStoreRepositoryShardCountComputedOncePerIndexTests.java
@@ -231,35 +231,32 @@ private List createIndicesAndSnapshots(int numberOfIndices, int numberOf
firstBatchOfSnapshotNames
);
- List secondBatchOfSnapshotNamesToDelete = new ArrayList<>();
- if (numberOfIndicesRecreated > 0) {
- // Now delete a random subset of indices, and then recreate them with the same name but a different shard count
- // This will force the new indices to have the same indexId but a different UUID
- List indicesToDelete = randomSubsetOf(numberOfIndicesRecreated, indexNames);
- for (String indexName : indicesToDelete) {
- deleteIndex(indexName);
- // Creates a new index with the same name but a different number of shards
- createIndex(indexName, indexSettings(between(4, 6), 0).build());
- ensureGreen(indexName);
- }
+ // Now delete a random subset of indices (this can be 0) and then recreate them with the same name but a different shard count
+ // This will force the new indices to have the same indexId but a different UUID
+ List indicesToDelete = randomSubsetOf(numberOfIndicesRecreated, indexNames);
+ for (String indexName : indicesToDelete) {
+ deleteIndex(indexName);
+ // Creates a new index with the same name but a different number of shards
+ createIndex(indexName, indexSettings(between(4, 6), 0).build());
+ ensureGreen(indexName);
+ }
- // Do the second batch of snapshots
- int numberOfSnapshotsInSecondBatch = randomIntBetween(3, 10);
- List secondBatchOfSnapshotNames = new ArrayList<>();
- for (int i = 0; i < numberOfSnapshotsInSecondBatch; i++) {
- String snapshotName = "second-snapshot-" + i;
- secondBatchOfSnapshotNames.add(snapshotName);
- client().admin()
- .cluster()
- .prepareCreateSnapshot(TEST_REQUEST_TIMEOUT, TEST_REPO_NAME, snapshotName)
- .setWaitForCompletion(true)
- .get();
- }
- secondBatchOfSnapshotNamesToDelete = randomSubsetOf(
- randomIntBetween(1, numberOfSnapshotsInSecondBatch - 1),
- secondBatchOfSnapshotNames
- );
+ // Do the second batch of snapshots, whether we've modified any indices or not
+ int numberOfSnapshotsInSecondBatch = randomIntBetween(3, 10);
+ List secondBatchOfSnapshotNames = new ArrayList<>();
+ for (int i = 0; i < numberOfSnapshotsInSecondBatch; i++) {
+ String snapshotName = "second-snapshot-" + i;
+ secondBatchOfSnapshotNames.add(snapshotName);
+ client().admin()
+ .cluster()
+ .prepareCreateSnapshot(TEST_REQUEST_TIMEOUT, TEST_REPO_NAME, snapshotName)
+ .setWaitForCompletion(true)
+ .get();
}
+ List secondBatchOfSnapshotNamesToDelete = randomSubsetOf(
+ randomIntBetween(1, numberOfSnapshotsInSecondBatch - 1),
+ secondBatchOfSnapshotNames
+ );
firstBatchOfSnapshotNamesToDelete.addAll(secondBatchOfSnapshotNamesToDelete);
return firstBatchOfSnapshotNamesToDelete;
From 19104111db2d5d81a0092cc461d176327959d9bd Mon Sep 17 00:00:00 2001
From: Joshua Adams
Date: Fri, 31 Oct 2025 13:43:00 +0000
Subject: [PATCH 13/17] Comments for IndexMetaDataGenerations
---
.../IndexMetaDataGenerations.java | 55 ++++++-------------
.../IndexMetaDataGenerationsTests.java | 7 +--
2 files changed, 20 insertions(+), 42 deletions(-)
diff --git a/server/src/main/java/org/elasticsearch/repositories/IndexMetaDataGenerations.java b/server/src/main/java/org/elasticsearch/repositories/IndexMetaDataGenerations.java
index 7ffe3ff3bf8e6..7da9bdda72aa0 100644
--- a/server/src/main/java/org/elasticsearch/repositories/IndexMetaDataGenerations.java
+++ b/server/src/main/java/org/elasticsearch/repositories/IndexMetaDataGenerations.java
@@ -9,7 +9,6 @@
package org.elasticsearch.repositories;
-import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.IndexMetadata;
import org.elasticsearch.common.util.Maps;
import org.elasticsearch.common.util.set.Sets;
@@ -24,6 +23,8 @@
import java.util.Set;
import java.util.stream.Collectors;
+import static org.elasticsearch.common.UUIDs.RANDOM_BASED_UUID_STRING_LENGTH;
+
/**
* Tracks the blob uuids of blobs containing {@link IndexMetadata} for snapshots as well an identifier for each of these blobs.
* Before writing a new {@link IndexMetadata} blob during snapshot finalization in
@@ -46,20 +47,12 @@ public final class IndexMetaDataGenerations {
*/
final Map identifiers;
- /**
- * Map of blob uuid to index metadata identifier. This is a reverse lookup of the identifiers map.
- */
- final Map blobUuidToIndexMetadataMap;
-
IndexMetaDataGenerations(Map> lookup, Map identifiers) {
assert identifiers.keySet().equals(lookup.values().stream().flatMap(m -> m.values().stream()).collect(Collectors.toSet()))
: "identifier mappings " + identifiers + " don't track the same blob ids as the lookup map " + lookup;
assert lookup.values().stream().noneMatch(Map::isEmpty) : "Lookup contained empty map [" + lookup + "]";
this.lookup = Map.copyOf(lookup);
this.identifiers = Map.copyOf(identifiers);
- this.blobUuidToIndexMetadataMap = identifiers.entrySet()
- .stream()
- .collect(Collectors.toUnmodifiableMap(Map.Entry::getValue, Map.Entry::getKey));
}
public boolean isEmpty() {
@@ -163,7 +156,7 @@ public IndexMetaDataGenerations withRemovedSnapshots(Collection snap
@Override
public int hashCode() {
- return Objects.hash(identifiers, lookup, blobUuidToIndexMetadataMap);
+ return Objects.hash(identifiers, lookup);
}
@Override
@@ -175,20 +168,12 @@ public boolean equals(Object that) {
return false;
}
final IndexMetaDataGenerations other = (IndexMetaDataGenerations) that;
- return lookup.equals(other.lookup)
- && identifiers.equals(other.identifiers)
- && blobUuidToIndexMetadataMap.equals(other.blobUuidToIndexMetadataMap);
+ return lookup.equals(other.lookup) && identifiers.equals(other.identifiers);
}
@Override
public String toString() {
- return "IndexMetaDataGenerations{lookup:"
- + lookup
- + "}{identifier:"
- + identifiers
- + "}{blobUuidToIndexMetadataMap:"
- + blobUuidToIndexMetadataMap
- + "}";
+ return "IndexMetaDataGenerations{lookup:" + lookup + "}{identifier:" + identifiers + "}";
}
/**
@@ -214,27 +199,23 @@ public static String buildUniqueIdentifier(IndexMetadata indexMetaData) {
/**
* Given a blobId, returns the index UUID associated with it.
- *
- * If the blobId is not found, returns null.
- *
- * If the index UUID is _na_ {@code ClusterState.UNKNOWN_UUID}
* @param blobId The blob ID
+ * @return the index UUID associated with the blobId, or null if the blobId is not found
*/
@Nullable
public String getIndexUUIDFromBlobId(String blobId) {
- // Find the unique identifier for this blobId
- String uniqueIdentifier = blobUuidToIndexMetadataMap.get(blobId);
- if (uniqueIdentifier == null) {
- return null;
- }
+ // Map of blob id to index uuid. This is a reverse lookup of the identifiers map.
+ final Map blobUuidToIndexUUIDMap = identifiers.entrySet()
+ .stream()
+ .collect(
+ Collectors.toUnmodifiableMap(
+ Map.Entry::getValue,
+ // Parses the index UUID from the beginning of the unique index metadata identifier
+ entry -> entry.getKey().substring(0, RANDOM_BASED_UUID_STRING_LENGTH)
+ )
+ );
- // The uniqueIdentifier is built in {@code buildUniqueIdentifier}, and is prefixed with indexUUID
- // The indexUUID is either a UUID of length 22, or _na_
- boolean na = uniqueIdentifier.startsWith(ClusterState.UNKNOWN_UUID + "-");
- if (na) {
- return ClusterState.UNKNOWN_UUID;
- }
- assert uniqueIdentifier.length() >= 22;
- return uniqueIdentifier.substring(0, 22);
+ // Find the unique identifier for this blobId
+ return blobUuidToIndexUUIDMap.get(blobId);
}
}
diff --git a/server/src/test/java/org/elasticsearch/repositories/IndexMetaDataGenerationsTests.java b/server/src/test/java/org/elasticsearch/repositories/IndexMetaDataGenerationsTests.java
index f1325e0d3161b..aa0169f0fb4f3 100644
--- a/server/src/test/java/org/elasticsearch/repositories/IndexMetaDataGenerationsTests.java
+++ b/server/src/test/java/org/elasticsearch/repositories/IndexMetaDataGenerationsTests.java
@@ -27,7 +27,6 @@ public class IndexMetaDataGenerationsTests extends ESTestCase {
public void testIndexMetaDataGenerations() {
Map identifiers = new HashMap<>();
Map lookupInternal = new HashMap<>();
- Map blobUuidToIndexMetadataMap = new HashMap<>();
int numberOfMetadataIdentifiers = randomIntBetween(5, 10);
for (int i = 0; i < numberOfMetadataIdentifiers; i++) {
@@ -35,7 +34,6 @@ public void testIndexMetaDataGenerations() {
String metaIdentifier = generateMetaIdentifier(indexUUID);
String blobUUID = randomAlphanumericOfLength(randomIntBetween(5, 10));
identifiers.put(metaIdentifier, blobUUID);
- blobUuidToIndexMetadataMap.put(blobUUID, metaIdentifier);
IndexId indexId = new IndexId(randomAlphanumericOfLength(10), indexUUID);
lookupInternal.put(indexId, metaIdentifier);
@@ -48,7 +46,6 @@ public void testIndexMetaDataGenerations() {
assertEquals(lookup, generations.lookup);
assertEquals(identifiers, generations.identifiers);
- assertEquals(blobUuidToIndexMetadataMap, generations.blobUuidToIndexMetadataMap);
}
public void testBuildUniqueIdentifierWithAllFieldsPresent() {
@@ -76,7 +73,7 @@ public void testBuildUniqueIdentifierWithMissingHistoryUUID() {
assertEquals(indexUUID + "-_na_-" + settingsVersion + "-" + mappingVersion + "-" + aliasesVersion, result);
}
- public void testConvertBlobIdToIndexUUIDReturnsIndexUUID() {
+ public void testGetIndexUUIDFromBlobIdReturnsIndexUUID() {
String indexUUID = generateUUID();
String randomSetting = randomAlphaOfLength(randomIntBetween(5, 10));
long settingsVersion = randomNonNegativeLong();
@@ -94,7 +91,7 @@ public void testConvertBlobIdToIndexUUIDReturnsIndexUUID() {
assertEquals(indexUUID, generations.getIndexUUIDFromBlobId(blobId));
}
- public void testConvertBlobIdToIndexUUIDReturnsNullWhenBlobIdIsNotFound() {
+ public void testGetIndexUUIDFromBlobIdReturnsNullWhenBlobIdIsNotFound() {
IndexMetaDataGenerations generations = new IndexMetaDataGenerations(Map.of(), Map.of());
assertNull(generations.getIndexUUIDFromBlobId(randomAlphanumericOfLength(randomIntBetween(5, 10))));
}
From 434b88f8d61a86c0bba59e36a53adf3813c5404c Mon Sep 17 00:00:00 2001
From: Joshua Adams
Date: Wed, 5 Nov 2025 15:03:15 +0000
Subject: [PATCH 14/17] David Comments
---
.../repositories/IndexMetaDataGenerations.java | 14 ++++----------
.../blobstore/BlobStoreRepository.java | 9 +++++----
.../IndexMetaDataGenerationsTests.java | 13 ++++++++-----
.../elasticsearch/test/ESSingleNodeTestCase.java | 2 --
4 files changed, 17 insertions(+), 21 deletions(-)
diff --git a/server/src/main/java/org/elasticsearch/repositories/IndexMetaDataGenerations.java b/server/src/main/java/org/elasticsearch/repositories/IndexMetaDataGenerations.java
index 7da9bdda72aa0..c92551425d0c4 100644
--- a/server/src/main/java/org/elasticsearch/repositories/IndexMetaDataGenerations.java
+++ b/server/src/main/java/org/elasticsearch/repositories/IndexMetaDataGenerations.java
@@ -198,14 +198,11 @@ public static String buildUniqueIdentifier(IndexMetadata indexMetaData) {
}
/**
- * Given a blobId, returns the index UUID associated with it.
- * @param blobId The blob ID
- * @return the index UUID associated with the blobId, or null if the blobId is not found
+ * Generates a map of blob id to Index UUID. This is a reverse lookup of {@code identifiers}
+ * @return A map of blob id to index UUID
*/
- @Nullable
- public String getIndexUUIDFromBlobId(String blobId) {
- // Map of blob id to index uuid. This is a reverse lookup of the identifiers map.
- final Map blobUuidToIndexUUIDMap = identifiers.entrySet()
+ public Map getBlobIdToIndexUuidMap() {
+ return identifiers.entrySet()
.stream()
.collect(
Collectors.toUnmodifiableMap(
@@ -214,8 +211,5 @@ public String getIndexUUIDFromBlobId(String blobId) {
entry -> entry.getKey().substring(0, RANDOM_BASED_UUID_STRING_LENGTH)
)
);
-
- // Find the unique identifier for this blobId
- return blobUuidToIndexUUIDMap.get(blobId);
}
}
diff --git a/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java b/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java
index 9058e6496e9b3..63750b59eef18 100644
--- a/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java
+++ b/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java
@@ -1312,6 +1312,7 @@ void run(ActionListener listener) {
private void determineShardCount(ActionListener listener) {
try (var listeners = new RefCountingListener(listener)) {
+ Map blobIdToindexUuidMap = originalRepositoryData.indexMetaDataGenerations().getBlobIdToIndexUuidMap();
for (final var blobId : snapshotIds.stream()
.filter(snapshotsWithIndex::contains)
.map(id -> originalRepositoryData.indexMetaDataGenerations().indexMetaBlobId(id, indexId))
@@ -1320,11 +1321,11 @@ private void determineShardCount(ActionListener listener) {
// index UUID; the shard count is going to be the same for all metadata with the same index UUID, so it is
// unnecessary to read multiple metadata blobs corresponding to the same index UUID.
// NB if the index metadata blob is in the pre-7.9.0 format then this will return null
- String indexUUID = originalRepositoryData.indexMetaDataGenerations().getIndexUUIDFromBlobId(blobId);
+ String indexUUID = blobIdToindexUuidMap.get(blobId);
- // Without an index UUID, we don't know if we've encountered this index before and must read it's IndexMetadata
- // from heap. If this is a new index UUID, with a possibly higher shard count, then we also need to read
- // it's IndexMetadata from heap
+ // Without an index UUID, we don't know if we've encountered this index before and must read its IndexMetadata
+ // from heap. If this is a new index UUID, it could have a higher shard count, so we also need to read
+ // its IndexMetadata from heap
if (indexUUID == null || indexUUIDs.add(indexUUID)) {
snapshotExecutor.execute(ActionRunnable.run(listeners.acquire(), () -> {
try {
diff --git a/server/src/test/java/org/elasticsearch/repositories/IndexMetaDataGenerationsTests.java b/server/src/test/java/org/elasticsearch/repositories/IndexMetaDataGenerationsTests.java
index aa0169f0fb4f3..1e6f74dcb1786 100644
--- a/server/src/test/java/org/elasticsearch/repositories/IndexMetaDataGenerationsTests.java
+++ b/server/src/test/java/org/elasticsearch/repositories/IndexMetaDataGenerationsTests.java
@@ -16,6 +16,7 @@
import org.elasticsearch.snapshots.SnapshotId;
import org.elasticsearch.test.ESTestCase;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@@ -73,7 +74,7 @@ public void testBuildUniqueIdentifierWithMissingHistoryUUID() {
assertEquals(indexUUID + "-_na_-" + settingsVersion + "-" + mappingVersion + "-" + aliasesVersion, result);
}
- public void testGetIndexUUIDFromBlobIdReturnsIndexUUID() {
+ public void testGetBlobIdToIndexUuidMap() {
String indexUUID = generateUUID();
String randomSetting = randomAlphaOfLength(randomIntBetween(5, 10));
long settingsVersion = randomNonNegativeLong();
@@ -88,16 +89,18 @@ public void testGetIndexUUIDFromBlobIdReturnsIndexUUID() {
Map> lookup = Map.of(snapshotId, Map.of(indexId, uniqueIdentifier));
IndexMetaDataGenerations generations = new IndexMetaDataGenerations(lookup, Map.of(uniqueIdentifier, blobId));
- assertEquals(indexUUID, generations.getIndexUUIDFromBlobId(blobId));
+
+ Map expectedBlobIdToindexUuidMap = Map.of(blobId, indexUUID);
+ assertEquals(expectedBlobIdToindexUuidMap, generations.getBlobIdToIndexUuidMap());
}
- public void testGetIndexUUIDFromBlobIdReturnsNullWhenBlobIdIsNotFound() {
+ public void testGetBlobIdToIndexUuidMapWithNoIdentifierMap() {
IndexMetaDataGenerations generations = new IndexMetaDataGenerations(Map.of(), Map.of());
- assertNull(generations.getIndexUUIDFromBlobId(randomAlphanumericOfLength(randomIntBetween(5, 10))));
+ assertEquals(Collections.emptyMap(), generations.getBlobIdToIndexUuidMap());
}
private String generateUUID() {
- return usually() ? UUIDs.randomBase64UUID(random()) : ClusterState.UNKNOWN_UUID;
+ return UUIDs.randomBase64UUID(random());
}
private String generateMetaIdentifier(String indexUUID) {
diff --git a/test/framework/src/main/java/org/elasticsearch/test/ESSingleNodeTestCase.java b/test/framework/src/main/java/org/elasticsearch/test/ESSingleNodeTestCase.java
index a71f2b9a2c0a1..b947dfc13ecfe 100644
--- a/test/framework/src/main/java/org/elasticsearch/test/ESSingleNodeTestCase.java
+++ b/test/framework/src/main/java/org/elasticsearch/test/ESSingleNodeTestCase.java
@@ -411,8 +411,6 @@ protected IndexService createIndex(String index, CreateIndexRequestBuilder creat
*/
protected void deleteIndex(String index) {
assertAcked(indicesAdmin().prepareDelete(index).get());
- // Wait for the cluster to be green after deletion
- ensureGreen();
}
public Index resolveIndex(String index) {
From 830d64b710b57dfc6956c71162e6bc131e18c892 Mon Sep 17 00:00:00 2001
From: Joshua Adams
Date: Wed, 5 Nov 2025 15:07:57 +0000
Subject: [PATCH 15/17] Fixing comment and variable name
---
.../elasticsearch/repositories/IndexMetaDataGenerations.java | 1 -
.../repositories/blobstore/BlobStoreRepository.java | 4 ++--
2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/server/src/main/java/org/elasticsearch/repositories/IndexMetaDataGenerations.java b/server/src/main/java/org/elasticsearch/repositories/IndexMetaDataGenerations.java
index c92551425d0c4..39f5f4c03088d 100644
--- a/server/src/main/java/org/elasticsearch/repositories/IndexMetaDataGenerations.java
+++ b/server/src/main/java/org/elasticsearch/repositories/IndexMetaDataGenerations.java
@@ -185,7 +185,6 @@ public String toString() {
* @return identifier string
*/
public static String buildUniqueIdentifier(IndexMetadata indexMetaData) {
- // If modifying this identifier, then also extend the getIndexUUIDFromBlobId function below
return indexMetaData.getIndexUUID()
+ "-"
+ indexMetaData.getSettings().get(IndexMetadata.SETTING_HISTORY_UUID, IndexMetadata.INDEX_UUID_NA_VALUE)
diff --git a/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java b/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java
index 63750b59eef18..18ba1a6662e26 100644
--- a/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java
+++ b/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java
@@ -1312,7 +1312,7 @@ void run(ActionListener listener) {
private void determineShardCount(ActionListener listener) {
try (var listeners = new RefCountingListener(listener)) {
- Map blobIdToindexUuidMap = originalRepositoryData.indexMetaDataGenerations().getBlobIdToIndexUuidMap();
+ Map blobIdToIndexUuidMap = originalRepositoryData.indexMetaDataGenerations().getBlobIdToIndexUuidMap();
for (final var blobId : snapshotIds.stream()
.filter(snapshotsWithIndex::contains)
.map(id -> originalRepositoryData.indexMetaDataGenerations().indexMetaBlobId(id, indexId))
@@ -1321,7 +1321,7 @@ private void determineShardCount(ActionListener listener) {
// index UUID; the shard count is going to be the same for all metadata with the same index UUID, so it is
// unnecessary to read multiple metadata blobs corresponding to the same index UUID.
// NB if the index metadata blob is in the pre-7.9.0 format then this will return null
- String indexUUID = blobIdToindexUuidMap.get(blobId);
+ String indexUUID = blobIdToIndexUuidMap.get(blobId);
// Without an index UUID, we don't know if we've encountered this index before and must read its IndexMetadata
// from heap. If this is a new index UUID, it could have a higher shard count, so we also need to read
From b8bd53c7800bbcd0133878d2a381ce97f7f37681 Mon Sep 17 00:00:00 2001
From: elasticsearchmachine
Date: Wed, 5 Nov 2025 15:15:37 +0000
Subject: [PATCH 16/17] [CI] Auto commit changes from spotless
---
.../repositories/IndexMetaDataGenerationsTests.java | 1 -
1 file changed, 1 deletion(-)
diff --git a/server/src/test/java/org/elasticsearch/repositories/IndexMetaDataGenerationsTests.java b/server/src/test/java/org/elasticsearch/repositories/IndexMetaDataGenerationsTests.java
index 1e6f74dcb1786..676de332c7c70 100644
--- a/server/src/test/java/org/elasticsearch/repositories/IndexMetaDataGenerationsTests.java
+++ b/server/src/test/java/org/elasticsearch/repositories/IndexMetaDataGenerationsTests.java
@@ -9,7 +9,6 @@
package org.elasticsearch.repositories;
-import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.IndexMetadata;
import org.elasticsearch.common.UUIDs;
import org.elasticsearch.common.settings.Settings;
From 42a2830283046dfdf6ce9ecb5da292b2ab325225 Mon Sep 17 00:00:00 2001
From: Joshua Adams
Date: Wed, 12 Nov 2025 11:17:42 +0000
Subject: [PATCH 17/17] Move blobIdToIndexUuidMap int snapshots deletion
---
.../repositories/IndexMetaDataGenerations.java | 2 +-
.../repositories/blobstore/BlobStoreRepository.java | 7 ++++++-
.../repositories/IndexMetaDataGenerationsTests.java | 1 -
3 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/server/src/main/java/org/elasticsearch/repositories/IndexMetaDataGenerations.java b/server/src/main/java/org/elasticsearch/repositories/IndexMetaDataGenerations.java
index 39f5f4c03088d..c1b3ca100b492 100644
--- a/server/src/main/java/org/elasticsearch/repositories/IndexMetaDataGenerations.java
+++ b/server/src/main/java/org/elasticsearch/repositories/IndexMetaDataGenerations.java
@@ -197,7 +197,7 @@ public static String buildUniqueIdentifier(IndexMetadata indexMetaData) {
}
/**
- * Generates a map of blob id to Index UUID. This is a reverse lookup of {@code identifiers}
+ * Generates a map of blob id to Index UUID. This is a reverse lookup of {@link #identifiers}
* @return A map of blob id to index UUID
*/
public Map getBlobIdToIndexUuidMap() {
diff --git a/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java b/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java
index 18ba1a6662e26..1de597bbc38d2 100644
--- a/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java
+++ b/server/src/main/java/org/elasticsearch/repositories/blobstore/BlobStoreRepository.java
@@ -1125,6 +1125,11 @@ class SnapshotsDeletion {
*/
private final ShardBlobsToDelete shardBlobsToDelete = new ShardBlobsToDelete();
+ /**
+ * A map of blob id to index UUID
+ */
+ private final Map blobIdToIndexUuidMap;
+
SnapshotsDeletion(
Collection snapshotIds,
long originalRepositoryDataGeneration,
@@ -1140,6 +1145,7 @@ class SnapshotsDeletion {
this.originalRootBlobs = originalRootBlobs;
this.originalIndexContainers = originalIndexContainers;
this.originalRepositoryData = originalRepositoryData;
+ this.blobIdToIndexUuidMap = originalRepositoryData.indexMetaDataGenerations().getBlobIdToIndexUuidMap();
}
// ---------------------------------------------------------------------------------------------------------------------------------
@@ -1312,7 +1318,6 @@ void run(ActionListener listener) {
private void determineShardCount(ActionListener listener) {
try (var listeners = new RefCountingListener(listener)) {
- Map blobIdToIndexUuidMap = originalRepositoryData.indexMetaDataGenerations().getBlobIdToIndexUuidMap();
for (final var blobId : snapshotIds.stream()
.filter(snapshotsWithIndex::contains)
.map(id -> originalRepositoryData.indexMetaDataGenerations().indexMetaBlobId(id, indexId))
diff --git a/server/src/test/java/org/elasticsearch/repositories/IndexMetaDataGenerationsTests.java b/server/src/test/java/org/elasticsearch/repositories/IndexMetaDataGenerationsTests.java
index 1e6f74dcb1786..676de332c7c70 100644
--- a/server/src/test/java/org/elasticsearch/repositories/IndexMetaDataGenerationsTests.java
+++ b/server/src/test/java/org/elasticsearch/repositories/IndexMetaDataGenerationsTests.java
@@ -9,7 +9,6 @@
package org.elasticsearch.repositories;
-import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.IndexMetadata;
import org.elasticsearch.common.UUIDs;
import org.elasticsearch.common.settings.Settings;