Skip to content

Commit 4a0b3c3

Browse files
Download functionality of global metadata from remote store (opensearch-project#10535) (opensearch-project#10733)
* Download funcationality of global metadata from remote store (cherry picked from commit 3a36c22) Signed-off-by: Dhwanil Patel <dhwanip@amazon.com> Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent 5e79291 commit 4a0b3c3

5 files changed

Lines changed: 168 additions & 19 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
99
- [Admission control] Add enhancements to FS stats to include read/write time, queue size and IO time ([#10696](https://github.com/opensearch-project/OpenSearch/pull/10696))
1010
- [Remote cluster state] Change file names for remote cluster state ([#10557](https://github.com/opensearch-project/OpenSearch/pull/10557))
1111
- [Remote cluster state] Upload global metadata in cluster state to remote store([#10404](https://github.com/opensearch-project/OpenSearch/pull/10404))
12+
- [Remote cluster state] Download functionality of global metadata from remote store ([#10535](https://github.com/opensearch-project/OpenSearch/pull/10535))
1213

1314
### Dependencies
1415
- Bumps jetty version to 9.4.52.v20230823 to fix GMS-2023-1857 ([#9822](https://github.com/opensearch-project/OpenSearch/pull/9822))

server/src/internalClusterTest/java/org/opensearch/gateway/remote/RemoteClusterStateServiceIT.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,10 @@ public void testFullClusterRestoreStaleDelete() throws Exception {
8686

8787
assertEquals(10, repository.blobStore().blobContainer(baseMetadataPath.add("manifest")).listBlobsByPrefix("manifest").size());
8888

89-
Map<String, IndexMetadata> indexMetadataMap = remoteClusterStateService.getLatestIndexMetadata(
89+
Map<String, IndexMetadata> indexMetadataMap = remoteClusterStateService.getLatestMetadata(
9090
cluster().getClusterName(),
9191
getClusterState().metadata().clusterUUID()
92-
);
92+
).getIndices();
9393
assertEquals(0, indexMetadataMap.values().stream().findFirst().get().getNumberOfReplicas());
9494
assertEquals(shardCount, indexMetadataMap.values().stream().findFirst().get().getNumberOfShards());
9595
}

server/src/main/java/org/opensearch/gateway/remote/RemoteClusterStateService.java

Lines changed: 55 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -662,18 +662,18 @@ private BlobPath getManifestFolderPath(String clusterName, String clusterUUID) {
662662
*
663663
* @param clusterUUID uuid of cluster state to refer to in remote
664664
* @param clusterName name of the cluster
665+
* @param clusterMetadataManifest manifest file of cluster
665666
* @return {@code Map<String, IndexMetadata>} latest IndexUUID to IndexMetadata map
666667
*/
667-
public Map<String, IndexMetadata> getLatestIndexMetadata(String clusterName, String clusterUUID) throws IOException {
668-
start();
669-
Map<String, IndexMetadata> remoteIndexMetadata = new HashMap<>();
670-
Optional<ClusterMetadataManifest> clusterMetadataManifest = getLatestClusterMetadataManifest(clusterName, clusterUUID);
671-
if (!clusterMetadataManifest.isPresent()) {
672-
throw new IllegalStateException("Latest index metadata is not present for the provided clusterUUID");
673-
}
674-
assert Objects.equals(clusterUUID, clusterMetadataManifest.get().getClusterUUID())
668+
private Map<String, IndexMetadata> getIndexMetadataMap(
669+
String clusterName,
670+
String clusterUUID,
671+
ClusterMetadataManifest clusterMetadataManifest
672+
) {
673+
assert Objects.equals(clusterUUID, clusterMetadataManifest.getClusterUUID())
675674
: "Corrupt ClusterMetadataManifest found. Cluster UUID mismatch.";
676-
for (UploadedIndexMetadata uploadedIndexMetadata : clusterMetadataManifest.get().getIndices()) {
675+
Map<String, IndexMetadata> remoteIndexMetadata = new HashMap<>();
676+
for (UploadedIndexMetadata uploadedIndexMetadata : clusterMetadataManifest.getIndices()) {
677677
IndexMetadata indexMetadata = getIndexMetadata(clusterName, clusterUUID, uploadedIndexMetadata);
678678
remoteIndexMetadata.put(uploadedIndexMetadata.getIndexUUID(), indexMetadata);
679679
}
@@ -704,6 +704,52 @@ private IndexMetadata getIndexMetadata(String clusterName, String clusterUUID, U
704704
}
705705
}
706706

707+
/**
708+
* Fetch latest metadata from remote cluster state including global metadata and index metadata
709+
*
710+
* @param clusterUUID uuid of cluster state to refer to in remote
711+
* @param clusterName name of the cluster
712+
* @return {@link IndexMetadata}
713+
*/
714+
public Metadata getLatestMetadata(String clusterName, String clusterUUID) throws IOException {
715+
start();
716+
Optional<ClusterMetadataManifest> clusterMetadataManifest = getLatestClusterMetadataManifest(clusterName, clusterUUID);
717+
if (!clusterMetadataManifest.isPresent()) {
718+
throw new IllegalStateException(
719+
String.format(Locale.ROOT, "Latest cluster metadata manifest is not present for the provided clusterUUID: %s", clusterUUID)
720+
);
721+
}
722+
// Fetch Global Metadata
723+
Metadata globalMetadata = getGlobalMetadata(clusterName, clusterUUID, clusterMetadataManifest.get());
724+
725+
// Fetch Index Metadata
726+
Map<String, IndexMetadata> indices = getIndexMetadataMap(clusterName, clusterUUID, clusterMetadataManifest.get());
727+
728+
return Metadata.builder(globalMetadata).indices(indices).build();
729+
}
730+
731+
private Metadata getGlobalMetadata(String clusterName, String clusterUUID, ClusterMetadataManifest clusterMetadataManifest) {
732+
String globalMetadataFileName = clusterMetadataManifest.getGlobalMetadataFileName();
733+
try {
734+
// Fetch Global metadata
735+
if (globalMetadataFileName != null) {
736+
String[] splitPath = globalMetadataFileName.split("/");
737+
return GLOBAL_METADATA_FORMAT.read(
738+
globalMetadataContainer(clusterName, clusterUUID),
739+
splitPath[splitPath.length - 1],
740+
blobStoreRepository.getNamedXContentRegistry()
741+
);
742+
} else {
743+
return Metadata.EMPTY_METADATA;
744+
}
745+
} catch (IOException e) {
746+
throw new IllegalStateException(
747+
String.format(Locale.ROOT, "Error while downloading Global Metadata - %s", globalMetadataFileName),
748+
e
749+
);
750+
}
751+
}
752+
707753
/**
708754
* Fetch latest ClusterMetadataManifest from remote state store
709755
*

server/src/main/java/org/opensearch/index/recovery/RemoteStoreRestoreService.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,8 @@ public RemoteRestoreResult restore(
141141
|| restoreClusterUUID.isBlank()) == false;
142142
if (metadataFromRemoteStore) {
143143
try {
144-
remoteClusterStateService.getLatestIndexMetadata(currentState.getClusterName().value(), restoreClusterUUID)
144+
remoteClusterStateService.getLatestMetadata(currentState.getClusterName().value(), restoreClusterUUID)
145+
.getIndices()
145146
.values()
146147
.forEach(indexMetadata -> {
147148
indexMetadataMap.put(indexMetadata.getIndex().getName(), new Tuple<>(true, indexMetadata));

server/src/test/java/org/opensearch/gateway/remote/RemoteClusterStateServiceTests.java

Lines changed: 108 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import org.opensearch.cluster.ClusterName;
1313
import org.opensearch.cluster.ClusterState;
1414
import org.opensearch.cluster.coordination.CoordinationMetadata;
15+
import org.opensearch.cluster.metadata.IndexGraveyard;
1516
import org.opensearch.cluster.metadata.IndexMetadata;
1617
import org.opensearch.cluster.metadata.Metadata;
1718
import org.opensearch.cluster.node.DiscoveryNodes;
@@ -28,6 +29,7 @@
2829
import org.opensearch.common.lucene.store.ByteArrayIndexInput;
2930
import org.opensearch.common.settings.ClusterSettings;
3031
import org.opensearch.common.settings.Settings;
32+
import org.opensearch.core.ParseField;
3133
import org.opensearch.core.action.ActionListener;
3234
import org.opensearch.core.common.bytes.BytesArray;
3335
import org.opensearch.core.common.bytes.BytesReference;
@@ -634,7 +636,8 @@ public void testReadLatestMetadataManifestSuccessButNoIndexMetadata() throws IOE
634636

635637
remoteClusterStateService.start();
636638
assertEquals(
637-
remoteClusterStateService.getLatestIndexMetadata(clusterState.getClusterName().value(), clusterState.metadata().clusterUUID())
639+
remoteClusterStateService.getLatestMetadata(clusterState.getClusterName().value(), clusterState.metadata().clusterUUID())
640+
.getIndices()
638641
.size(),
639642
0
640643
);
@@ -662,10 +665,8 @@ public void testReadLatestMetadataManifestSuccessButIndexMetadataFetchIOExceptio
662665
remoteClusterStateService.start();
663666
Exception e = assertThrows(
664667
IllegalStateException.class,
665-
() -> remoteClusterStateService.getLatestIndexMetadata(
666-
clusterState.getClusterName().value(),
667-
clusterState.metadata().clusterUUID()
668-
)
668+
() -> remoteClusterStateService.getLatestMetadata(clusterState.getClusterName().value(), clusterState.metadata().clusterUUID())
669+
.getIndices()
669670
);
670671
assertEquals(e.getMessage(), "Error while downloading IndexMetadata - " + uploadedIndexMetadata.getUploadedFilename());
671672
}
@@ -704,6 +705,70 @@ public void testReadLatestMetadataManifestSuccess() throws IOException {
704705
assertThat(manifest.getStateUUID(), is(expectedManifest.getStateUUID()));
705706
}
706707

708+
public void testReadGlobalMetadata() throws IOException {
709+
when(blobStoreRepository.getNamedXContentRegistry()).thenReturn(new NamedXContentRegistry(
710+
List.of(new NamedXContentRegistry.Entry(Metadata.Custom.class, new ParseField(IndexGraveyard.TYPE), IndexGraveyard::fromXContent))));
711+
final ClusterState clusterState = generateClusterStateWithGlobalMetadata().nodes(nodesWithLocalNodeClusterManager()).build();
712+
remoteClusterStateService.start();
713+
714+
final ClusterMetadataManifest expectedManifest = ClusterMetadataManifest.builder()
715+
.indices(List.of())
716+
.clusterTerm(1L)
717+
.stateVersion(1L)
718+
.stateUUID("state-uuid")
719+
.clusterUUID("cluster-uuid")
720+
.codecVersion(MANIFEST_CURRENT_CODEC_VERSION)
721+
.globalMetadataFileName("global-metadata-file")
722+
.nodeId("nodeA")
723+
.opensearchVersion(VersionUtils.randomOpenSearchVersion(random()))
724+
.previousClusterUUID("prev-cluster-uuid")
725+
.build();
726+
727+
Metadata expactedMetadata = Metadata.builder().persistentSettings(Settings.builder().put("readonly", true).build()).build();
728+
mockBlobContainerForGlobalMetadata(mockBlobStoreObjects(), expectedManifest, expactedMetadata);
729+
730+
Metadata metadata = remoteClusterStateService.getLatestMetadata(
731+
clusterState.getClusterName().value(),
732+
clusterState.metadata().clusterUUID()
733+
);
734+
735+
assertTrue(Metadata.isGlobalStateEquals(metadata, expactedMetadata));
736+
}
737+
738+
public void testReadGlobalMetadataIOException() throws IOException {
739+
final ClusterState clusterState = generateClusterStateWithGlobalMetadata().nodes(nodesWithLocalNodeClusterManager()).build();
740+
remoteClusterStateService.start();
741+
String globalIndexMetadataName = "global-metadata-file";
742+
final ClusterMetadataManifest expectedManifest = ClusterMetadataManifest.builder()
743+
.indices(List.of())
744+
.clusterTerm(1L)
745+
.stateVersion(1L)
746+
.stateUUID("state-uuid")
747+
.clusterUUID("cluster-uuid")
748+
.codecVersion(MANIFEST_CURRENT_CODEC_VERSION)
749+
.globalMetadataFileName(globalIndexMetadataName)
750+
.nodeId("nodeA")
751+
.opensearchVersion(VersionUtils.randomOpenSearchVersion(random()))
752+
.previousClusterUUID("prev-cluster-uuid")
753+
.build();
754+
755+
Metadata expactedMetadata = Metadata.builder().persistentSettings(Settings.builder().put("readonly", true).build()).build();
756+
757+
BlobContainer blobContainer = mockBlobStoreObjects();
758+
mockBlobContainerForGlobalMetadata(blobContainer, expectedManifest, expactedMetadata);
759+
760+
when(blobContainer.readBlob(RemoteClusterStateService.GLOBAL_METADATA_FORMAT.blobName(globalIndexMetadataName))).thenThrow(
761+
FileNotFoundException.class
762+
);
763+
764+
remoteClusterStateService.start();
765+
Exception e = assertThrows(
766+
IllegalStateException.class,
767+
() -> remoteClusterStateService.getLatestMetadata(clusterState.getClusterName().value(), clusterState.metadata().clusterUUID())
768+
);
769+
assertEquals(e.getMessage(), "Error while downloading Global Metadata - " + globalIndexMetadataName);
770+
}
771+
707772
public void testReadLatestIndexMetadataSuccess() throws IOException {
708773
final ClusterState clusterState = generateClusterStateWithOneIndex().nodes(nodesWithLocalNodeClusterManager()).build();
709774
remoteClusterStateService.start();
@@ -730,15 +795,16 @@ public void testReadLatestIndexMetadataSuccess() throws IOException {
730795
.nodeId("nodeA")
731796
.opensearchVersion(VersionUtils.randomOpenSearchVersion(random()))
732797
.previousClusterUUID("prev-cluster-uuid")
798+
.globalMetadataFileName("global-metadata-file")
733799
.codecVersion(ClusterMetadataManifest.CODEC_V0)
734800
.build();
735801

736802
mockBlobContainer(mockBlobStoreObjects(), expectedManifest, Map.of(index.getUUID(), indexMetadata));
737803

738-
Map<String, IndexMetadata> indexMetadataMap = remoteClusterStateService.getLatestIndexMetadata(
804+
Map<String, IndexMetadata> indexMetadataMap = remoteClusterStateService.getLatestMetadata(
739805
clusterState.getClusterName().value(),
740806
clusterState.metadata().clusterUUID()
741-
);
807+
).getIndices();
742808

743809
assertEquals(indexMetadataMap.size(), 1);
744810
assertEquals(indexMetadataMap.get(index.getUUID()).getIndex().getName(), index.getName());
@@ -1114,6 +1180,41 @@ private void mockBlobContainer(
11141180
});
11151181
}
11161182

1183+
private void mockBlobContainerForGlobalMetadata(
1184+
BlobContainer blobContainer,
1185+
ClusterMetadataManifest clusterMetadataManifest,
1186+
Metadata metadata
1187+
) throws IOException {
1188+
String mockManifestFileName = "manifest__1__2__C__456__1";
1189+
BlobMetadata blobMetadata = new PlainBlobMetadata(mockManifestFileName, 1);
1190+
when(
1191+
blobContainer.listBlobsByPrefixInSortedOrder(
1192+
"manifest" + RemoteClusterStateService.DELIMITER,
1193+
1,
1194+
BlobContainer.BlobNameSortOrder.LEXICOGRAPHIC
1195+
)
1196+
).thenReturn(Arrays.asList(blobMetadata));
1197+
1198+
BytesReference bytes = RemoteClusterStateService.CLUSTER_METADATA_MANIFEST_FORMAT.serialize(
1199+
clusterMetadataManifest,
1200+
mockManifestFileName,
1201+
blobStoreRepository.getCompressor(),
1202+
FORMAT_PARAMS
1203+
);
1204+
when(blobContainer.readBlob(mockManifestFileName)).thenReturn(new ByteArrayInputStream(bytes.streamInput().readAllBytes()));
1205+
1206+
BytesReference bytesGlobalMetadata = RemoteClusterStateService.GLOBAL_METADATA_FORMAT.serialize(
1207+
metadata,
1208+
"global-metadata-file",
1209+
blobStoreRepository.getCompressor(),
1210+
FORMAT_PARAMS
1211+
);
1212+
String[] splitPath = clusterMetadataManifest.getGlobalMetadataFileName().split("/");
1213+
when(blobContainer.readBlob(RemoteClusterStateService.GLOBAL_METADATA_FORMAT.blobName(splitPath[splitPath.length - 1]))).thenReturn(
1214+
new ByteArrayInputStream(bytesGlobalMetadata.streamInput().readAllBytes())
1215+
);
1216+
}
1217+
11171218
private static ClusterState.Builder generateClusterStateWithGlobalMetadata() {
11181219
final Settings clusterSettings = Settings.builder().put("cluster.blocks.read_only", true).build();
11191220
final CoordinationMetadata coordinationMetadata = CoordinationMetadata.builder().term(1L).build();

0 commit comments

Comments
 (0)