Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Enhancement] Verify the chunk size of the snapshot when restoring a searchable snapshot #11741

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.carrotsearch.randomizedtesting.annotations.ThreadLeakFilters;
import com.carrotsearch.randomizedtesting.generators.RandomPicks;

import org.opensearch.action.admin.cluster.allocation.ClusterAllocationExplainResponse;
import org.opensearch.action.admin.cluster.node.stats.NodeStats;
import org.opensearch.action.admin.cluster.node.stats.NodesStatsRequest;
import org.opensearch.action.admin.cluster.node.stats.NodesStatsResponse;
Expand All @@ -16,6 +17,7 @@
import org.opensearch.action.admin.cluster.snapshots.delete.DeleteSnapshotRequest;
import org.opensearch.action.admin.cluster.snapshots.get.GetSnapshotsResponse;
import org.opensearch.action.admin.cluster.snapshots.restore.RestoreSnapshotRequest;
import org.opensearch.action.admin.cluster.snapshots.restore.RestoreSnapshotResponse;
import org.opensearch.action.admin.indices.settings.get.GetSettingsRequest;
import org.opensearch.action.admin.indices.settings.get.GetSettingsResponse;
import org.opensearch.action.admin.indices.settings.put.UpdateSettingsRequestBuilder;
Expand All @@ -35,6 +37,7 @@
import org.opensearch.index.IndexModule;
import org.opensearch.index.IndexNotFoundException;
import org.opensearch.index.store.remote.file.CleanerDaemonThreadLeakFilter;
import org.opensearch.index.store.remote.file.OnDemandBlockSnapshotIndexInput;
import org.opensearch.index.store.remote.filecache.FileCacheStats;
import org.opensearch.monitor.fs.FsInfo;
import org.opensearch.node.Node;
Expand Down Expand Up @@ -75,10 +78,10 @@ protected Settings.Builder randomRepositorySettings() {
return settings;
}

private Settings.Builder chunkedRepositorySettings() {
private Settings.Builder chunkedRepositorySettings(long chunkSize) {
final Settings.Builder settings = Settings.builder();
settings.put("location", randomRepoPath()).put("compress", randomBoolean());
settings.put("chunk_size", 2 << 23, ByteSizeUnit.BYTES);
settings.put("chunk_size", chunkSize, ByteSizeUnit.BYTES);
return settings;
}

Expand Down Expand Up @@ -195,7 +198,8 @@ public void testCreateSearchableSnapshotWithChunks() throws Exception {
final String snapshotName = "test-snap";
final Client client = client();

Settings.Builder repositorySettings = chunkedRepositorySettings();
final int blockSize = OnDemandBlockSnapshotIndexInput.Builder.DEFAULT_BLOCK_SIZE;
Settings.Builder repositorySettings = chunkedRepositorySettings(blockSize * randomLongBetween(1, 100));

internalCluster().ensureAtLeastNumSearchAndDataNodes(numReplicasIndex + 1);
createIndexWithDocsAndEnsureGreen(numReplicasIndex, 1000, indexName);
Expand All @@ -209,6 +213,39 @@ public void testCreateSearchableSnapshotWithChunks() throws Exception {
assertDocCount(restoredIndexName, 1000L);
}

/**
* Tests a chunked repository scenario where the chunk size of repo is not a multiple of the block size.
*/
public void testCreateSearchableSnapshotWithChunksThrowExceptions() throws Exception {
final int numReplicasIndex = randomIntBetween(1, 4);
final String indexName = "test-idx";
final String restoredIndexName = indexName + "-copy";
final String repoName = "test-repo";
final String snapshotName = "test-snap";
final Client client = client();

final int blockSize = OnDemandBlockSnapshotIndexInput.Builder.DEFAULT_BLOCK_SIZE;
Settings.Builder repositorySettings = chunkedRepositorySettings(blockSize + 1);

internalCluster().ensureAtLeastNumSearchAndDataNodes(numReplicasIndex + 1);
createIndexWithDocsAndEnsureGreen(numReplicasIndex, 1000, indexName);
createRepositoryWithSettings(repositorySettings, repoName);
takeSnapshot(client, snapshotName, repoName, indexName);

deleteIndicesAndEnsureGreen(client, indexName);
RestoreSnapshotResponse restoreSnapshotResponse = restoreSnapshot(client, snapshotName, repoName);
assertEquals(0, restoreSnapshotResponse.getRestoreInfo().successfulShards());

ClusterAllocationExplainResponse explainResponse = client.admin().cluster().prepareAllocationExplain().execute().actionGet();
String detailsMessage = explainResponse.getExplanation().getUnassignedInfo().getDetails();
assert detailsMessage != null;
assertTrue(
detailsMessage.contains(
"the chunk size of snapshot used for searchable snapshot index must be either the Long.MAX_VALUE or a multiple of the OnDemandBlockIndexInput block size"
)
);
}

/**
* Tests the functionality of remote shard allocation to
* ensure it can assign remote shards to a node with local shards given it has the
Expand Down Expand Up @@ -399,6 +436,19 @@ private void restoreSnapshotAndEnsureGreen(Client client, String snapshotName, S
ensureGreen();
}

private RestoreSnapshotResponse restoreSnapshot(Client client, String snapshotName, String repoName) {
logger.info("--> restore indices as 'remote_snapshot'");
return client.admin()
.cluster()
.prepareRestoreSnapshot(repoName, snapshotName)
.setRenamePattern("(.+)")
.setRenameReplacement("$1-copy")
.setStorageType(RestoreSnapshotRequest.StorageType.REMOTE_SNAPSHOT)
.setWaitForCompletion(true)
.execute()
.actionGet();
}

private void assertRemoteSnapshotIndexSettings(Client client, String... snapshotIndexNames) {
GetSettingsResponse settingsResponse = client.admin()
.indices()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,18 @@
private final TransferManager transferManager;

public RemoteSnapshotDirectory(BlobStoreIndexShardSnapshot snapshot, FSDirectory localStoreDir, TransferManager transferManager) {
final long chunkSize = snapshot.indexFiles().isEmpty() ? 0L : snapshot.indexFiles().get(0).partSize().getBytes();
final long blockSize = OnDemandBlockSnapshotIndexInput.Builder.DEFAULT_BLOCK_SIZE;

Check warning on line 51 in server/src/main/java/org/opensearch/index/store/remote/directory/RemoteSnapshotDirectory.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/org/opensearch/index/store/remote/directory/RemoteSnapshotDirectory.java#L51

Added line #L51 was not covered by tests
if ((chunkSize & (blockSize - 1)) != 0 && chunkSize != Long.MAX_VALUE) {
throw new IllegalArgumentException(

Check warning on line 53 in server/src/main/java/org/opensearch/index/store/remote/directory/RemoteSnapshotDirectory.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/org/opensearch/index/store/remote/directory/RemoteSnapshotDirectory.java#L53

Added line #L53 was not covered by tests
"the chunk size of snapshot used for searchable snapshot index must be either the Long.MAX_VALUE or a multiple of the OnDemandBlockIndexInput block size, got chunk size ["
+ chunkSize
+ "], and block size ["
+ blockSize
+ "]"
);
}

this.fileInfoMap = snapshot.indexFiles()
.stream()
.collect(Collectors.toMap(BlobStoreIndexShardSnapshot.FileInfo::physicalName, f -> f));
Expand Down
Loading