From 99e3dddfe69ed40486a47ae81e90347bf08f4721 Mon Sep 17 00:00:00 2001 From: Sachin Kale Date: Mon, 23 Oct 2023 17:02:44 +0530 Subject: [PATCH] Add support to create empty local translog if remote translog is empty Signed-off-by: Sachin Kale --- .../restore/RestoreRemoteStoreRequest.java | 32 ++++++++++++++++++- .../cluster/routing/RecoverySource.java | 30 +++++++++++++++-- .../opensearch/gateway/GatewayMetaState.java | 1 + .../recovery/RemoteStoreRestoreService.java | 17 +++++++--- .../opensearch/index/shard/StoreRecovery.java | 13 ++++++-- .../GatewayMetaStatePersistedStateTests.java | 18 +++++++---- 6 files changed, 95 insertions(+), 16 deletions(-) diff --git a/server/src/main/java/org/opensearch/action/admin/cluster/remotestore/restore/RestoreRemoteStoreRequest.java b/server/src/main/java/org/opensearch/action/admin/cluster/remotestore/restore/RestoreRemoteStoreRequest.java index 3b090415c175b..60928d641c2ab 100644 --- a/server/src/main/java/org/opensearch/action/admin/cluster/remotestore/restore/RestoreRemoteStoreRequest.java +++ b/server/src/main/java/org/opensearch/action/admin/cluster/remotestore/restore/RestoreRemoteStoreRequest.java @@ -8,6 +8,7 @@ package org.opensearch.action.admin.cluster.remotestore.restore; +import org.opensearch.Version; import org.opensearch.action.ActionRequestValidationException; import org.opensearch.action.support.IndicesOptions; import org.opensearch.action.support.clustermanager.ClusterManagerNodeRequest; @@ -37,6 +38,7 @@ public class RestoreRemoteStoreRequest extends ClusterManagerNodeRequest> indexMetadataMap = new HashMap<>(); @@ -176,7 +183,7 @@ public RemoteRestoreResult restore( } } } - return executeRestore(currentState, indexMetadataMap, restoreAllShards, remoteMetadata); + return executeRestore(currentState, indexMetadataMap, restoreAllShards, remoteMetadata, forceEmptyTranslog); } /** @@ -190,7 +197,8 @@ private RemoteRestoreResult executeRestore( ClusterState currentState, Map> indexMetadataMap, boolean restoreAllShards, - Metadata remoteMetadata + Metadata remoteMetadata, + boolean forceEmptyTranslog ) { final String restoreUUID = UUIDs.randomBase64UUID(); List indicesToBeRestored = new ArrayList<>(); @@ -228,7 +236,8 @@ private RemoteRestoreResult executeRestore( RecoverySource.RemoteStoreRecoverySource recoverySource = new RecoverySource.RemoteStoreRecoverySource( restoreUUID, updatedIndexMetadata.getCreationVersion(), - indexId + indexId, + forceEmptyTranslog ); rtBuilder.addAsRemoteStoreRestore(updatedIndexMetadata, recoverySource, indexShardRoutingTableMap, restoreAllShards); diff --git a/server/src/main/java/org/opensearch/index/shard/StoreRecovery.java b/server/src/main/java/org/opensearch/index/shard/StoreRecovery.java index c0211e1257c8e..6ff7a2449dd70 100644 --- a/server/src/main/java/org/opensearch/index/shard/StoreRecovery.java +++ b/server/src/main/java/org/opensearch/index/shard/StoreRecovery.java @@ -76,6 +76,7 @@ import java.io.IOException; import java.nio.channels.FileChannel; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.Arrays; @@ -538,16 +539,24 @@ private void recoverFromRemoteStore(IndexShard indexShard) throws IndexShardReco indexShard.syncSegmentsFromRemoteSegmentStore(true); indexShard.syncTranslogFilesFromRemoteTranslog(); + Path location = indexShard.shardPath().resolveTranslog(); + // On index creation, the only segment file that is created is segments_N. We can safely discard this file // as there is no data associated with this shard as part of segments. - if (store.directory().listAll().length <= 1) { - Path location = indexShard.shardPath().resolveTranslog(); + boolean remoteSegmentEmpty = store.directory().listAll().length <= 1; + boolean remoteTranslogEmpty = Files.exists(location.resolve(CHECKPOINT_FILE_NAME)) == false; + + if (remoteSegmentEmpty && remoteTranslogEmpty == false) { Checkpoint checkpoint = Checkpoint.read(location.resolve(CHECKPOINT_FILE_NAME)); final Path translogFile = location.resolve(Translog.getFilename(checkpoint.getGeneration())); try (FileChannel channel = FileChannel.open(translogFile, StandardOpenOption.READ)) { TranslogHeader translogHeader = TranslogHeader.read(translogFile, channel); store.createEmpty(indexShard.indexSettings().getIndexVersionCreated().luceneVersion, translogHeader.getTranslogUUID()); } + } else if (remoteSegmentEmpty == false && remoteTranslogEmpty) { + if (((RecoverySource.RemoteStoreRecoverySource) indexShard.shardRouting.recoverySource()).forceEmptyTranslog()) { + bootstrap(indexShard, store); + } } assert indexShard.shardRouting.primary() : "only primary shards can recover from store"; diff --git a/server/src/test/java/org/opensearch/gateway/GatewayMetaStatePersistedStateTests.java b/server/src/test/java/org/opensearch/gateway/GatewayMetaStatePersistedStateTests.java index 1d5c2a0f01b5c..588b91000fb71 100644 --- a/server/src/test/java/org/opensearch/gateway/GatewayMetaStatePersistedStateTests.java +++ b/server/src/test/java/org/opensearch/gateway/GatewayMetaStatePersistedStateTests.java @@ -785,7 +785,7 @@ public void testGatewayForRemoteState() throws IOException { RemoteClusterStateService remoteClusterStateService = mock(RemoteClusterStateService.class); when(remoteClusterStateService.getLastKnownUUIDFromRemote("test-cluster")).thenReturn("test-cluster-uuid"); RemoteStoreRestoreService remoteStoreRestoreService = mock(RemoteStoreRestoreService.class); - when(remoteStoreRestoreService.restore(any(), any(), anyBoolean(), any())).thenReturn( + when(remoteStoreRestoreService.restore(any(), any(), anyBoolean(), anyBoolean(), any())).thenReturn( RemoteRestoreResult.build("test-cluster-uuid", null, ClusterState.EMPTY_STATE) ); gateway = new MockGatewayMetaState(localNode, bigArrays, remoteClusterStateService, remoteStoreRestoreService); @@ -832,7 +832,7 @@ public void testGatewayForRemoteStateForInitialBootstrap() throws IOException { when(remoteClusterStateService.getLastKnownUUIDFromRemote(clusterName.value())).thenReturn(ClusterState.UNKNOWN_UUID); final RemoteStoreRestoreService remoteStoreRestoreService = mock(RemoteStoreRestoreService.class); - when(remoteStoreRestoreService.restore(any(), any(), anyBoolean(), any())).thenReturn( + when(remoteStoreRestoreService.restore(any(), any(), anyBoolean(), anyBoolean(), any())).thenReturn( RemoteRestoreResult.build("test-cluster-uuid", null, ClusterState.EMPTY_STATE) ); final PersistedStateRegistry persistedStateRegistry = persistedStateRegistry(); @@ -879,7 +879,7 @@ public void testGatewayForRemoteStateForNodeReplacement() throws IOException { ); final RemoteStoreRestoreService remoteStoreRestoreService = mock(RemoteStoreRestoreService.class); - when(remoteStoreRestoreService.restore(any(), any(), anyBoolean(), any())).thenReturn( + when(remoteStoreRestoreService.restore(any(), any(), anyBoolean(), anyBoolean(), any())).thenReturn( RemoteRestoreResult.build("test-cluster-uuid", null, previousState) ); final PersistedStateRegistry persistedStateRegistry = persistedStateRegistry(); @@ -893,7 +893,7 @@ public void testGatewayForRemoteStateForNodeReplacement() throws IOException { final CoordinationState.PersistedState lucenePersistedState = gateway.getPersistedState(); PersistedState remotePersistedState = persistedStateRegistry.getPersistedState(PersistedStateType.REMOTE); verify(remoteClusterStateService).getLastKnownUUIDFromRemote(Mockito.any()); - verify(remoteStoreRestoreService).restore(any(), any(), anyBoolean(), any()); + verify(remoteStoreRestoreService).restore(any(), any(), anyBoolean(), anyBoolean(), any()); assertThat(remotePersistedState.getLastAcceptedState(), nullValue()); assertThat(lucenePersistedState.getLastAcceptedState().metadata(), equalTo(previousState.metadata())); } finally { @@ -969,7 +969,7 @@ public void testGatewayForRemoteStateForInitialBootstrapBlocksApplied() throws I ).nodes(DiscoveryNodes.EMPTY_NODES).build(); final RemoteStoreRestoreService remoteStoreRestoreService = mock(RemoteStoreRestoreService.class); - when(remoteStoreRestoreService.restore(any(), any(), anyBoolean(), any())).thenReturn( + when(remoteStoreRestoreService.restore(any(), any(), anyBoolean(), anyBoolean(), any())).thenReturn( RemoteRestoreResult.build("test-cluster-uuid", null, clusterState) ); final PersistedStateRegistry persistedStateRegistry = persistedStateRegistry(); @@ -983,7 +983,13 @@ public void testGatewayForRemoteStateForInitialBootstrapBlocksApplied() throws I PersistedState remotePersistedState = persistedStateRegistry.getPersistedState(PersistedStateType.REMOTE); PersistedState lucenePersistedState = persistedStateRegistry.getPersistedState(PersistedStateType.LOCAL); verify(remoteClusterStateService).getLastKnownUUIDFromRemote(clusterName.value()); // change this - verify(remoteStoreRestoreService).restore(any(ClusterState.class), any(String.class), anyBoolean(), any(String[].class)); + verify(remoteStoreRestoreService).restore( + any(ClusterState.class), + any(String.class), + anyBoolean(), + anyBoolean(), + any(String[].class) + ); assertThat(remotePersistedState.getLastAcceptedState(), nullValue()); assertThat( Metadata.isGlobalStateEquals(lucenePersistedState.getLastAcceptedState().metadata(), clusterState.metadata()),