From b0951b0dc5db5a8d471993b1ac6a0dc34f9fd844 Mon Sep 17 00:00:00 2001 From: Varun Bansal Date: Wed, 12 Apr 2023 18:59:04 +0530 Subject: [PATCH] [Remote Store] Add support to create index with remote store by default (#6932) Signed-off-by: Varun Bansal --- distribution/src/config/opensearch.yml | 13 + ...CreateRemoteIndexClusterDefaultDocRep.java | 93 ++++ .../remotestore/CreateRemoteIndexIT.java | 423 ++++++++++++++++++ .../CreateRemoteIndexTranslogDisabledIT.java | 71 +++ .../cluster/metadata/IndexMetadata.java | 2 +- .../metadata/MetadataCreateIndexService.java | 79 ++++ .../common/settings/ClusterSettings.java | 16 +- .../common/settings/SettingsModule.java | 4 +- .../opensearch/indices/IndicesService.java | 40 ++ .../MetadataCreateIndexServiceTests.java | 286 ++++++++++++ .../opensearch/index/IndexSettingsTests.java | 4 +- 11 files changed, 1021 insertions(+), 10 deletions(-) create mode 100644 server/src/internalClusterTest/java/org/opensearch/remotestore/CreateRemoteIndexClusterDefaultDocRep.java create mode 100644 server/src/internalClusterTest/java/org/opensearch/remotestore/CreateRemoteIndexIT.java create mode 100644 server/src/internalClusterTest/java/org/opensearch/remotestore/CreateRemoteIndexTranslogDisabledIT.java diff --git a/distribution/src/config/opensearch.yml b/distribution/src/config/opensearch.yml index 7fc1e74ad6337..c2d84c6c71582 100644 --- a/distribution/src/config/opensearch.yml +++ b/distribution/src/config/opensearch.yml @@ -87,6 +87,19 @@ ${path.logs} # #action.destructive_requires_name: true # +# ---------------------------------- Remote Store ----------------------------------- +# Controls whether cluster imposes index creation only with remote store enabled +# cluster.remote_store.enabled: true +# +# Repository to use for segment upload while enforcing remote store for an index +# cluster.remote_store.repository: my-repo-1 +# +# Controls whether cluster imposes index creation only with translog remote store enabled +# cluster.remote_store.translog.enabled: true +# +# Repository to use for translog upload while enforcing remote store for an index +# cluster.remote_store.translog.repository: my-repo-1 +# # ---------------------------------- Experimental Features ----------------------------------- # # Gates the visibility of the index setting that allows changing of replication type. diff --git a/server/src/internalClusterTest/java/org/opensearch/remotestore/CreateRemoteIndexClusterDefaultDocRep.java b/server/src/internalClusterTest/java/org/opensearch/remotestore/CreateRemoteIndexClusterDefaultDocRep.java new file mode 100644 index 0000000000000..41f47bd3683e4 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/remotestore/CreateRemoteIndexClusterDefaultDocRep.java @@ -0,0 +1,93 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.remotestore; + +import org.opensearch.action.admin.indices.get.GetIndexRequest; +import org.opensearch.action.admin.indices.get.GetIndexResponse; +import org.opensearch.cluster.metadata.IndexMetadata; +import org.opensearch.common.settings.Settings; +import org.opensearch.indices.replication.common.ReplicationType; +import org.opensearch.test.OpenSearchIntegTestCase; + +import static org.hamcrest.Matchers.containsString; +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_REMOTE_TRANSLOG_STORE_ENABLED; +import static org.opensearch.indices.IndicesService.CLUSTER_REPLICATION_TYPE_SETTING; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; + +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST) +public class CreateRemoteIndexClusterDefaultDocRep extends CreateRemoteIndexIT { + + @Override + protected Settings nodeSettings(int nodeOriginal) { + Settings settings = super.nodeSettings(nodeOriginal); + Settings.Builder builder = Settings.builder() + .put(settings) + .put(CLUSTER_REPLICATION_TYPE_SETTING.getKey(), ReplicationType.DOCUMENT); + return builder.build(); + } + + @Override + public void testRemoteStoreTranslogDisabledByUser() throws Exception { + final int numReplicas = internalCluster().numDataNodes(); + Settings settings = Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 3) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, numReplicas) + .put(IndexMetadata.SETTING_REPLICATION_TYPE, ReplicationType.SEGMENT) + .put(SETTING_REMOTE_TRANSLOG_STORE_ENABLED, false) + .build(); + assertAcked(client().admin().indices().prepareCreate("test-idx-1").setSettings(settings).get()); + GetIndexResponse getIndexResponse = client().admin() + .indices() + .getIndex(new GetIndexRequest().indices("test-idx-1").includeDefaults(true)) + .get(); + Settings indexSettings = getIndexResponse.settings().get("test-idx-1"); + verifyRemoteStoreIndexSettings(indexSettings, "true", "my-segment-repo-1", "false", null, ReplicationType.SEGMENT.toString(), null); + } + + @Override + public void testDefaultRemoteStoreNoUserOverride() throws Exception { + final int numReplicas = internalCluster().numDataNodes(); + Settings settings = Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 3) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, numReplicas) + .build(); + IllegalArgumentException exc = expectThrows( + IllegalArgumentException.class, + () -> client().admin().indices().prepareCreate("test-idx-1").setSettings(settings).get() + ); + assertThat( + exc.getMessage(), + containsString("Cannot enable [index.remote_store.enabled] when [cluster.indices.replication.strategy] is DOCUMENT") + ); + } + + public void testDefaultRemoteStoreNoUserOverrideExceptReplicationTypeSegment() throws Exception { + final int numReplicas = internalCluster().numDataNodes(); + Settings settings = Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 3) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, numReplicas) + .put(IndexMetadata.SETTING_REPLICATION_TYPE, ReplicationType.SEGMENT) + .build(); + assertAcked(client().admin().indices().prepareCreate("test-idx-1").setSettings(settings).get()); + GetIndexResponse getIndexResponse = client().admin() + .indices() + .getIndex(new GetIndexRequest().indices("test-idx-1").includeDefaults(true)) + .get(); + Settings indexSettings = getIndexResponse.settings().get("test-idx-1"); + verifyRemoteStoreIndexSettings( + indexSettings, + "true", + "my-segment-repo-1", + "true", + "my-translog-repo-1", + ReplicationType.SEGMENT.toString(), + null + ); + } +} diff --git a/server/src/internalClusterTest/java/org/opensearch/remotestore/CreateRemoteIndexIT.java b/server/src/internalClusterTest/java/org/opensearch/remotestore/CreateRemoteIndexIT.java new file mode 100644 index 0000000000000..5069c517494a4 --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/remotestore/CreateRemoteIndexIT.java @@ -0,0 +1,423 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.remotestore; + +import org.junit.Before; +import org.opensearch.action.admin.indices.get.GetIndexRequest; +import org.opensearch.action.admin.indices.get.GetIndexResponse; +import org.opensearch.cluster.metadata.IndexMetadata; +import org.opensearch.common.settings.Settings; +import org.opensearch.common.util.FeatureFlags; +import org.opensearch.indices.replication.common.ReplicationType; +import org.opensearch.test.FeatureFlagSetter; +import org.opensearch.test.OpenSearchIntegTestCase; + +import java.nio.file.Path; + +import static org.hamcrest.Matchers.containsString; +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_REMOTE_STORE_ENABLED; +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_REMOTE_STORE_REPOSITORY; +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_REMOTE_TRANSLOG_BUFFER_INTERVAL; +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_REMOTE_TRANSLOG_STORE_ENABLED; +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_REMOTE_TRANSLOG_STORE_REPOSITORY; +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_REPLICATION_TYPE; +import static org.opensearch.indices.IndicesService.CLUSTER_REMOTE_STORE_REPOSITORY_SETTING; +import static org.opensearch.indices.IndicesService.CLUSTER_REMOTE_TRANSLOG_REPOSITORY_SETTING; +import static org.opensearch.indices.IndicesService.CLUSTER_REMOTE_STORE_ENABLED_SETTING; +import static org.opensearch.indices.IndicesService.CLUSTER_REMOTE_TRANSLOG_STORE_ENABLED_SETTING; +import static org.opensearch.indices.IndicesService.CLUSTER_REPLICATION_TYPE_SETTING; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; + +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST) +public class CreateRemoteIndexIT extends OpenSearchIntegTestCase { + + @Override + protected Settings nodeSettings(int nodeOriginal) { + Settings settings = super.nodeSettings(nodeOriginal); + Settings.Builder builder = Settings.builder() + .put(CLUSTER_REPLICATION_TYPE_SETTING.getKey(), ReplicationType.SEGMENT) + .put(CLUSTER_REMOTE_STORE_ENABLED_SETTING.getKey(), true) + .put(CLUSTER_REMOTE_STORE_REPOSITORY_SETTING.getKey(), "my-segment-repo-1") + .put(CLUSTER_REMOTE_TRANSLOG_STORE_ENABLED_SETTING.getKey(), true) + .put(CLUSTER_REMOTE_TRANSLOG_REPOSITORY_SETTING.getKey(), "my-translog-repo-1") + .put(settings); + return builder.build(); + } + + @Override + protected Settings featureFlagSettings() { + return Settings.builder() + .put(super.featureFlagSettings()) + .put(FeatureFlags.SEGMENT_REPLICATION_EXPERIMENTAL, "true") + .put(FeatureFlags.REPLICATION_TYPE, "true") + .put(FeatureFlags.REMOTE_STORE, "true") + .build(); + } + + @Before + public void setup() { + FeatureFlagSetter.set(FeatureFlags.REMOTE_STORE); + FeatureFlagSetter.set(FeatureFlags.REPLICATION_TYPE); + internalCluster().startClusterManagerOnlyNode(); + Path absolutePath = randomRepoPath().toAbsolutePath(); + assertAcked( + clusterAdmin().preparePutRepository("my-segment-repo-1") + .setType("fs") + .setSettings(Settings.builder().put("location", absolutePath)) + ); + assertAcked( + clusterAdmin().preparePutRepository("my-translog-repo-1") + .setType("fs") + .setSettings(Settings.builder().put("location", absolutePath)) + ); + assertAcked( + clusterAdmin().preparePutRepository("my-custom-repo") + .setType("fs") + .setSettings(Settings.builder().put("location", absolutePath)) + ); + } + + public void testDefaultRemoteStoreNoUserOverride() throws Exception { + final int numReplicas = internalCluster().numDataNodes(); + Settings settings = Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 3) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, numReplicas) + .build(); + assertAcked(client().admin().indices().prepareCreate("test-idx-1").setSettings(settings).get()); + GetIndexResponse getIndexResponse = client().admin() + .indices() + .getIndex(new GetIndexRequest().indices("test-idx-1").includeDefaults(true)) + .get(); + Settings indexSettings = getIndexResponse.settings().get("test-idx-1"); + verifyRemoteStoreIndexSettings( + indexSettings, + "true", + "my-segment-repo-1", + "true", + "my-translog-repo-1", + ReplicationType.SEGMENT.toString(), + null + ); + } + + public void testRemoteStoreDisabledByUser() throws Exception { + final int numReplicas = internalCluster().numDataNodes(); + Settings settings = Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 3) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, numReplicas) + .put(SETTING_REMOTE_STORE_ENABLED, false) + .build(); + assertAcked(client().admin().indices().prepareCreate("test-idx-1").setSettings(settings).get()); + GetIndexResponse getIndexResponse = client().admin() + .indices() + .getIndex(new GetIndexRequest().indices("test-idx-1").includeDefaults(true)) + .get(); + Settings indexSettings = getIndexResponse.settings().get("test-idx-1"); + verifyRemoteStoreIndexSettings(indexSettings, "false", null, null, null, null, null); + } + + public void testRemoteStoreEnabledByUserWithoutRemoteRepoAndSegmentReplicationIllegalArgumentException() throws Exception { + final int numReplicas = internalCluster().numDataNodes(); + Settings settings = Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 3) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, numReplicas) + .put(SETTING_REMOTE_STORE_ENABLED, true) + .build(); + + IllegalArgumentException exc = expectThrows( + IllegalArgumentException.class, + () -> client().admin().indices().prepareCreate("test-idx-1").setSettings(settings).get() + ); + assertThat( + exc.getMessage(), + containsString("To enable index.remote_store.enabled, index.replication.type should be set to SEGMENT") + ); + } + + public void testRemoteStoreEnabledByUserWithoutRemoteRepoIllegalArgumentException() throws Exception { + final int numReplicas = internalCluster().numDataNodes(); + Settings settings = Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 3) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, numReplicas) + .put(SETTING_REPLICATION_TYPE, ReplicationType.SEGMENT) + .put(SETTING_REMOTE_STORE_ENABLED, true) + .build(); + + IllegalArgumentException exc = expectThrows( + IllegalArgumentException.class, + () -> client().admin().indices().prepareCreate("test-idx-1").setSettings(settings).get() + ); + assertThat( + exc.getMessage(), + containsString("Setting index.remote_store.repository should be provided with non-empty repository ID") + ); + } + + public void testReplicationTypeDocumentByUser() throws Exception { + final int numReplicas = internalCluster().numDataNodes(); + Settings settings = Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 3) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, numReplicas) + .put(SETTING_REPLICATION_TYPE, ReplicationType.DOCUMENT) + .build(); + assertAcked(client().admin().indices().prepareCreate("test-idx-1").setSettings(settings).get()); + GetIndexResponse getIndexResponse = client().admin() + .indices() + .getIndex(new GetIndexRequest().indices("test-idx-1").includeDefaults(true)) + .get(); + Settings indexSettings = getIndexResponse.settings().get("test-idx-1"); + verifyRemoteStoreIndexSettings(indexSettings, null, null, null, null, ReplicationType.DOCUMENT.toString(), null); + } + + public void testRemoteStoreSegmentRepoWithoutRemoteEnabledAndSegmentReplicationIllegalArgumentException() throws Exception { + final int numReplicas = internalCluster().numDataNodes(); + Settings settings = Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 3) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, numReplicas) + .put(SETTING_REMOTE_STORE_REPOSITORY, "my-custom-repo") + .build(); + IllegalArgumentException exc = expectThrows( + IllegalArgumentException.class, + () -> client().admin().indices().prepareCreate("test-idx-1").setSettings(settings).get() + ); + assertThat( + exc.getMessage(), + containsString("Settings index.remote_store.repository can only be set/enabled when index.remote_store.enabled is set to true") + ); + } + + public void testRemoteStoreEnabledByUserWithRemoteRepo() throws Exception { + final int numReplicas = internalCluster().numDataNodes(); + Settings settings = Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 3) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, numReplicas) + .put(SETTING_REPLICATION_TYPE, ReplicationType.SEGMENT) + .put(SETTING_REMOTE_STORE_ENABLED, true) + .put(SETTING_REMOTE_STORE_REPOSITORY, "my-custom-repo") + .build(); + + assertAcked(client().admin().indices().prepareCreate("test-idx-1").setSettings(settings).get()); + GetIndexResponse getIndexResponse = client().admin() + .indices() + .getIndex(new GetIndexRequest().indices("test-idx-1").includeDefaults(true)) + .get(); + Settings indexSettings = getIndexResponse.settings().get("test-idx-1"); + verifyRemoteStoreIndexSettings( + indexSettings, + "true", + "my-custom-repo", + "true", + "my-translog-repo-1", + ReplicationType.SEGMENT.toString(), + null + ); + } + + public void testRemoteStoreTranslogDisabledByUser() throws Exception { + final int numReplicas = internalCluster().numDataNodes(); + Settings settings = Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 3) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, numReplicas) + .put(SETTING_REMOTE_TRANSLOG_STORE_ENABLED, false) + .build(); + assertAcked(client().admin().indices().prepareCreate("test-idx-1").setSettings(settings).get()); + GetIndexResponse getIndexResponse = client().admin() + .indices() + .getIndex(new GetIndexRequest().indices("test-idx-1").includeDefaults(true)) + .get(); + Settings indexSettings = getIndexResponse.settings().get("test-idx-1"); + verifyRemoteStoreIndexSettings(indexSettings, "true", "my-segment-repo-1", "false", null, ReplicationType.SEGMENT.toString(), null); + } + + public void testRemoteStoreOverrideOnlyTranslogRepoIllegalArgumentException() throws Exception { + final int numReplicas = internalCluster().numDataNodes(); + Settings settings = Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 3) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, numReplicas) + .put(SETTING_REMOTE_TRANSLOG_STORE_REPOSITORY, "my-custom-repo") + .build(); + IllegalArgumentException exc = expectThrows( + IllegalArgumentException.class, + () -> client().admin().indices().prepareCreate("test-idx-1").setSettings(settings).get() + ); + assertThat( + exc.getMessage(), + containsString( + "Settings index.remote_store.translog.repository can only be set/enabled when index.remote_store.translog.enabled is set to true" + ) + ); + } + + public void testRemoteStoreOverrideOnlyTranslogEnabled() throws Exception { + final int numReplicas = internalCluster().numDataNodes(); + Settings settings = Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 3) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, numReplicas) + .put(SETTING_REMOTE_TRANSLOG_STORE_ENABLED, true) + .build(); + + IllegalArgumentException exc = expectThrows( + IllegalArgumentException.class, + () -> client().admin().indices().prepareCreate("test-idx-1").setSettings(settings).get() + ); + assertThat( + exc.getMessage(), + containsString( + "Settings index.remote_store.translog.enabled can only be set/enabled when index.remote_store.enabled is set to true" + ) + ); + } + + public void testRemoteStoreOverrideOnlyTranslogEnabledAndRepo() throws Exception { + final int numReplicas = internalCluster().numDataNodes(); + Settings settings = Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 3) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, numReplicas) + .put(SETTING_REMOTE_TRANSLOG_STORE_ENABLED, true) + .put(SETTING_REMOTE_TRANSLOG_STORE_REPOSITORY, "my-custom-repo") + .build(); + + IllegalArgumentException exc = expectThrows( + IllegalArgumentException.class, + () -> client().admin().indices().prepareCreate("test-idx-1").setSettings(settings).get() + ); + assertThat( + exc.getMessage(), + containsString( + "Settings index.remote_store.translog.enabled can only be set/enabled when index.remote_store.enabled is set to true" + ) + ); + } + + public void testRemoteStoreOverrideTranslogDisabledCorrectly() throws Exception { + final int numReplicas = internalCluster().numDataNodes(); + Settings settings = Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 3) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, numReplicas) + .put(SETTING_REPLICATION_TYPE, ReplicationType.SEGMENT) + .put(SETTING_REMOTE_STORE_ENABLED, true) + .put(SETTING_REMOTE_STORE_REPOSITORY, "my-custom-repo") + .put(SETTING_REMOTE_TRANSLOG_STORE_ENABLED, false) + .build(); + assertAcked(client().admin().indices().prepareCreate("test-idx-1").setSettings(settings).get()); + GetIndexResponse getIndexResponse = client().admin() + .indices() + .getIndex(new GetIndexRequest().indices("test-idx-1").includeDefaults(true)) + .get(); + Settings indexSettings = getIndexResponse.settings().get("test-idx-1"); + verifyRemoteStoreIndexSettings(indexSettings, "true", "my-custom-repo", "false", null, ReplicationType.SEGMENT.toString(), null); + } + + public void testRemoteStoreOverrideTranslogDisabledWithTranslogRepoIllegalArgumentException() throws Exception { + final int numReplicas = internalCluster().numDataNodes(); + Settings settings = Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 3) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, numReplicas) + .put(SETTING_REPLICATION_TYPE, ReplicationType.SEGMENT) + .put(SETTING_REMOTE_STORE_ENABLED, true) + .put(SETTING_REMOTE_STORE_REPOSITORY, "my-custom-repo") + .put(SETTING_REMOTE_TRANSLOG_STORE_ENABLED, false) + .put(SETTING_REMOTE_TRANSLOG_STORE_REPOSITORY, "my-custom-repo") + .build(); + IllegalArgumentException exc = expectThrows( + IllegalArgumentException.class, + () -> client().admin().indices().prepareCreate("test-idx-1").setSettings(settings).get() + ); + assertThat( + exc.getMessage(), + containsString( + "Settings index.remote_store.translog.repository can only be set/enabled when index.remote_store.translog.enabled is set to true" + ) + ); + } + + public void testRemoteStoreOverrideOnlyTranslogRepoWithRemoteStoreEnabledIllegalArgumentException() throws Exception { + final int numReplicas = internalCluster().numDataNodes(); + Settings settings = Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 3) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, numReplicas) + .put(SETTING_REPLICATION_TYPE, ReplicationType.SEGMENT) + .put(SETTING_REMOTE_STORE_ENABLED, true) + .put(SETTING_REMOTE_STORE_REPOSITORY, "my-custom-repo") + .put(SETTING_REMOTE_TRANSLOG_STORE_REPOSITORY, "my-custom-repo") + .build(); + IllegalArgumentException exc = expectThrows( + IllegalArgumentException.class, + () -> client().admin().indices().prepareCreate("test-idx-1").setSettings(settings).get() + ); + assertThat( + exc.getMessage(), + containsString( + "Settings index.remote_store.translog.repository can only be set/enabled when index.remote_store.translog.enabled is set to true" + ) + ); + } + + public void testRemoteStoreOverrideTranslogRepoCorrectly() throws Exception { + final int numReplicas = internalCluster().numDataNodes(); + Settings settings = Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 3) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, numReplicas) + .put(SETTING_REPLICATION_TYPE, ReplicationType.SEGMENT) + .put(SETTING_REMOTE_STORE_ENABLED, true) + .put(SETTING_REMOTE_STORE_REPOSITORY, "my-custom-repo") + .put(SETTING_REMOTE_TRANSLOG_STORE_ENABLED, true) + .put(SETTING_REMOTE_TRANSLOG_STORE_REPOSITORY, "my-custom-repo") + .build(); + assertAcked(client().admin().indices().prepareCreate("test-idx-1").setSettings(settings).get()); + GetIndexResponse getIndexResponse = client().admin() + .indices() + .getIndex(new GetIndexRequest().indices("test-idx-1").includeDefaults(true)) + .get(); + Settings indexSettings = getIndexResponse.settings().get("test-idx-1"); + verifyRemoteStoreIndexSettings( + indexSettings, + "true", + "my-custom-repo", + "true", + "my-custom-repo", + ReplicationType.SEGMENT.toString(), + null + ); + } + + public void testRemoteStoreOverrideReplicationTypeIndexSettings() throws Exception { + final int numReplicas = internalCluster().numDataNodes(); + Settings settings = Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 3) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, numReplicas) + .put(SETTING_REPLICATION_TYPE, ReplicationType.DOCUMENT) + .build(); + assertAcked(client().admin().indices().prepareCreate("test-idx-1").setSettings(settings).get()); + GetIndexResponse getIndexResponse = client().admin() + .indices() + .getIndex(new GetIndexRequest().indices("test-idx-1").includeDefaults(true)) + .get(); + Settings indexSettings = getIndexResponse.settings().get("test-idx-1"); + verifyRemoteStoreIndexSettings(indexSettings, null, null, null, null, ReplicationType.DOCUMENT.toString(), null); + } + + protected void verifyRemoteStoreIndexSettings( + Settings indexSettings, + String isRemoteSegmentEnabled, + String remoteSegmentRepo, + String isRemoteTranslogEnabled, + String remoteTranslogRepo, + String replicationType, + String translogBufferInterval + ) { + assertEquals(replicationType, indexSettings.get(SETTING_REPLICATION_TYPE)); + assertEquals(isRemoteSegmentEnabled, indexSettings.get(SETTING_REMOTE_STORE_ENABLED)); + assertEquals(remoteSegmentRepo, indexSettings.get(SETTING_REMOTE_STORE_REPOSITORY)); + assertEquals(isRemoteTranslogEnabled, indexSettings.get(SETTING_REMOTE_TRANSLOG_STORE_ENABLED)); + assertEquals(remoteTranslogRepo, indexSettings.get(SETTING_REMOTE_TRANSLOG_STORE_REPOSITORY)); + assertEquals(translogBufferInterval, indexSettings.get(SETTING_REMOTE_TRANSLOG_BUFFER_INTERVAL)); + } + +} diff --git a/server/src/internalClusterTest/java/org/opensearch/remotestore/CreateRemoteIndexTranslogDisabledIT.java b/server/src/internalClusterTest/java/org/opensearch/remotestore/CreateRemoteIndexTranslogDisabledIT.java new file mode 100644 index 0000000000000..32f263425fa4e --- /dev/null +++ b/server/src/internalClusterTest/java/org/opensearch/remotestore/CreateRemoteIndexTranslogDisabledIT.java @@ -0,0 +1,71 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.remotestore; + +import org.opensearch.action.admin.indices.get.GetIndexRequest; +import org.opensearch.action.admin.indices.get.GetIndexResponse; +import org.opensearch.cluster.metadata.IndexMetadata; +import org.opensearch.common.settings.Settings; +import org.opensearch.indices.replication.common.ReplicationType; +import org.opensearch.test.OpenSearchIntegTestCase; + +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_REMOTE_STORE_ENABLED; +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_REMOTE_STORE_REPOSITORY; +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_REPLICATION_TYPE; +import static org.opensearch.indices.IndicesService.CLUSTER_REMOTE_TRANSLOG_REPOSITORY_SETTING; +import static org.opensearch.indices.IndicesService.CLUSTER_REMOTE_TRANSLOG_STORE_ENABLED_SETTING; +import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked; + +@OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST) +public class CreateRemoteIndexTranslogDisabledIT extends CreateRemoteIndexIT { + + @Override + protected Settings nodeSettings(int nodeOriginal) { + Settings settings = super.nodeSettings(nodeOriginal); + Settings.Builder builder = Settings.builder().put(settings); + builder.remove(CLUSTER_REMOTE_TRANSLOG_STORE_ENABLED_SETTING.getKey()); + builder.remove(CLUSTER_REMOTE_TRANSLOG_REPOSITORY_SETTING.getKey()); + return builder.build(); + } + + public void testRemoteStoreEnabledByUserWithRemoteRepo() throws Exception { + final int numReplicas = internalCluster().numDataNodes(); + Settings settings = Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 3) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, numReplicas) + .put(SETTING_REPLICATION_TYPE, ReplicationType.SEGMENT) + .put(SETTING_REMOTE_STORE_ENABLED, true) + .put(SETTING_REMOTE_STORE_REPOSITORY, "my-custom-repo") + .build(); + + assertAcked(client().admin().indices().prepareCreate("test-idx-1").setSettings(settings).get()); + GetIndexResponse getIndexResponse = client().admin() + .indices() + .getIndex(new GetIndexRequest().indices("test-idx-1").includeDefaults(true)) + .get(); + Settings indexSettings = getIndexResponse.settings().get("test-idx-1"); + verifyRemoteStoreIndexSettings(indexSettings, "true", "my-custom-repo", null, null, ReplicationType.SEGMENT.toString(), null); + } + + public void testDefaultRemoteStoreNoUserOverride() throws Exception { + final int numReplicas = internalCluster().numDataNodes(); + Settings settings = Settings.builder() + .put(IndexMetadata.SETTING_NUMBER_OF_SHARDS, 3) + .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, numReplicas) + .build(); + assertAcked(client().admin().indices().prepareCreate("test-idx-1").setSettings(settings).get()); + GetIndexResponse getIndexResponse = client().admin() + .indices() + .getIndex(new GetIndexRequest().indices("test-idx-1").includeDefaults(true)) + .get(); + Settings indexSettings = getIndexResponse.settings().get("test-idx-1"); + verifyRemoteStoreIndexSettings(indexSettings, "true", "my-segment-repo-1", null, null, ReplicationType.SEGMENT.toString(), null); + } + +} diff --git a/server/src/main/java/org/opensearch/cluster/metadata/IndexMetadata.java b/server/src/main/java/org/opensearch/cluster/metadata/IndexMetadata.java index 729882024831a..aa648a818cd15 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/IndexMetadata.java +++ b/server/src/main/java/org/opensearch/cluster/metadata/IndexMetadata.java @@ -381,7 +381,7 @@ private static void validateRemoteStoreSettingEnabled(final Map, Obje throw new IllegalArgumentException( "Settings " + setting.getKey() - + " can ont be set/enabled when " + + " can only be set/enabled when " + INDEX_REMOTE_STORE_ENABLED_SETTING.getKey() + " is set to true" ); diff --git a/server/src/main/java/org/opensearch/cluster/metadata/MetadataCreateIndexService.java b/server/src/main/java/org/opensearch/cluster/metadata/MetadataCreateIndexService.java index 42c8028ce7dfa..bc57b2ff56da3 100644 --- a/server/src/main/java/org/opensearch/cluster/metadata/MetadataCreateIndexService.java +++ b/server/src/main/java/org/opensearch/cluster/metadata/MetadataCreateIndexService.java @@ -91,6 +91,7 @@ import org.opensearch.indices.InvalidIndexNameException; import org.opensearch.indices.ShardLimitValidator; import org.opensearch.indices.SystemIndices; +import org.opensearch.indices.replication.common.ReplicationType; import org.opensearch.threadpool.ThreadPool; import java.io.IOException; @@ -104,6 +105,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; @@ -118,12 +120,27 @@ import static java.util.stream.Collectors.toList; import static org.opensearch.cluster.metadata.IndexMetadata.INDEX_NUMBER_OF_REPLICAS_SETTING; import static org.opensearch.cluster.metadata.IndexMetadata.INDEX_NUMBER_OF_SHARDS_SETTING; +import static org.opensearch.cluster.metadata.IndexMetadata.INDEX_REMOTE_STORE_ENABLED_SETTING; +import static org.opensearch.cluster.metadata.IndexMetadata.INDEX_REMOTE_STORE_REPOSITORY_SETTING; +import static org.opensearch.cluster.metadata.IndexMetadata.INDEX_REMOTE_TRANSLOG_REPOSITORY_SETTING; +import static org.opensearch.cluster.metadata.IndexMetadata.INDEX_REMOTE_TRANSLOG_STORE_ENABLED_SETTING; +import static org.opensearch.cluster.metadata.IndexMetadata.INDEX_REPLICATION_TYPE_SETTING; import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_AUTO_EXPAND_REPLICAS; import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_CREATION_DATE; import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_INDEX_UUID; import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_REPLICAS; import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_SHARDS; +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_REMOTE_STORE_ENABLED; +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_REMOTE_STORE_REPOSITORY; +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_REMOTE_TRANSLOG_STORE_ENABLED; +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_REMOTE_TRANSLOG_STORE_REPOSITORY; +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_REPLICATION_TYPE; import static org.opensearch.cluster.metadata.Metadata.DEFAULT_REPLICA_COUNT_SETTING; +import static org.opensearch.indices.IndicesService.CLUSTER_REMOTE_STORE_REPOSITORY_SETTING; +import static org.opensearch.indices.IndicesService.CLUSTER_REMOTE_TRANSLOG_REPOSITORY_SETTING; +import static org.opensearch.indices.IndicesService.CLUSTER_REMOTE_STORE_ENABLED_SETTING; +import static org.opensearch.indices.IndicesService.CLUSTER_REMOTE_TRANSLOG_STORE_ENABLED_SETTING; +import static org.opensearch.indices.IndicesService.CLUSTER_REPLICATION_TYPE_SETTING; /** * Service responsible for submitting create index requests @@ -874,6 +891,8 @@ static Settings aggregateIndexSettings( indexSettingsBuilder.put(IndexMetadata.SETTING_INDEX_PROVIDED_NAME, request.getProvidedName()); indexSettingsBuilder.put(SETTING_INDEX_UUID, UUIDs.randomBase64UUID()); + updateRemoteStoreSettings(indexSettingsBuilder, request.settings(), settings); + if (sourceMetadata != null) { assert request.resizeType() != null; prepareResizeIndexSettings( @@ -906,6 +925,66 @@ static Settings aggregateIndexSettings( return indexSettings; } + /** + * Updates index settings to enable remote store by default based on cluster level settings + * @param settingsBuilder index settings builder to be updated with relevant settings + * @param requestSettings settings passed in during index create request + * @param clusterSettings cluster level settings + */ + private static void updateRemoteStoreSettings(Settings.Builder settingsBuilder, Settings requestSettings, Settings clusterSettings) { + if (CLUSTER_REMOTE_STORE_ENABLED_SETTING.get(clusterSettings)) { + // Verify if we can create a remote store based index based on user provided settings + if (canCreateRemoteStoreIndex(requestSettings) == false) { + return; + } + + // Verify REPLICATION_TYPE cluster level setting is not conflicting with Remote Store + if (INDEX_REPLICATION_TYPE_SETTING.exists(requestSettings) == false + && CLUSTER_REPLICATION_TYPE_SETTING.get(clusterSettings).equals(ReplicationType.DOCUMENT)) { + throw new IllegalArgumentException( + "Cannot enable [" + + SETTING_REMOTE_STORE_ENABLED + + "] when [" + + CLUSTER_REPLICATION_TYPE_SETTING.getKey() + + "] is " + + ReplicationType.DOCUMENT + ); + } + + settingsBuilder.put(SETTING_REPLICATION_TYPE, ReplicationType.SEGMENT).put(SETTING_REMOTE_STORE_ENABLED, true); + + String remoteStoreRepo; + if (Objects.equals(requestSettings.get(INDEX_REMOTE_STORE_ENABLED_SETTING.getKey()), "true")) { + remoteStoreRepo = requestSettings.get(INDEX_REMOTE_STORE_REPOSITORY_SETTING.getKey()); + } else { + remoteStoreRepo = CLUSTER_REMOTE_STORE_REPOSITORY_SETTING.get(clusterSettings); + } + settingsBuilder.put(SETTING_REMOTE_STORE_REPOSITORY, remoteStoreRepo); + + boolean translogStoreEnabled = false; + String translogStoreRepo = null; + if (Objects.equals(requestSettings.get(INDEX_REMOTE_TRANSLOG_STORE_ENABLED_SETTING.getKey()), "true")) { + translogStoreEnabled = true; + translogStoreRepo = requestSettings.get(INDEX_REMOTE_TRANSLOG_REPOSITORY_SETTING.getKey()); + } else if (Objects.equals(requestSettings.get(INDEX_REMOTE_TRANSLOG_STORE_ENABLED_SETTING.getKey()), "false") == false + && CLUSTER_REMOTE_TRANSLOG_STORE_ENABLED_SETTING.get(clusterSettings)) { + translogStoreEnabled = true; + translogStoreRepo = CLUSTER_REMOTE_TRANSLOG_REPOSITORY_SETTING.get(clusterSettings); + } + if (translogStoreEnabled) { + settingsBuilder.put(SETTING_REMOTE_TRANSLOG_STORE_ENABLED, translogStoreEnabled) + .put(SETTING_REMOTE_TRANSLOG_STORE_REPOSITORY, translogStoreRepo); + } + } + } + + private static boolean canCreateRemoteStoreIndex(Settings requestSettings) { + return (INDEX_REPLICATION_TYPE_SETTING.exists(requestSettings) == false + || INDEX_REPLICATION_TYPE_SETTING.get(requestSettings).equals(ReplicationType.SEGMENT)) + && (INDEX_REMOTE_STORE_ENABLED_SETTING.exists(requestSettings) == false + || INDEX_REMOTE_STORE_ENABLED_SETTING.get(requestSettings)); + } + public static void validateStoreTypeSettings(Settings settings) { // deprecate simplefs store type: if (IndexModule.Type.SIMPLEFS.match(IndexModule.INDEX_STORE_TYPE_SETTING.get(settings))) { diff --git a/server/src/main/java/org/opensearch/common/settings/ClusterSettings.java b/server/src/main/java/org/opensearch/common/settings/ClusterSettings.java index efcddd9fa6075..8b60fd43ea991 100644 --- a/server/src/main/java/org/opensearch/common/settings/ClusterSettings.java +++ b/server/src/main/java/org/opensearch/common/settings/ClusterSettings.java @@ -643,11 +643,17 @@ public void apply(Settings value, Settings current, Settings previous) { * is ready for production release, the feature flag can be removed, and the * setting should be moved to {@link #BUILT_IN_CLUSTER_SETTINGS}. */ - public static final Map> FEATURE_FLAGGED_CLUSTER_SETTINGS = Map.of( - FeatureFlags.SEARCHABLE_SNAPSHOT, + public static final Map, List> FEATURE_FLAGGED_CLUSTER_SETTINGS = Map.of( + List.of(FeatureFlags.SEARCHABLE_SNAPSHOT), List.of(Node.NODE_SEARCH_CACHE_SIZE_SETTING), - FeatureFlags.SEGMENT_REPLICATION_EXPERIMENTAL, - List.of(IndicesService.CLUSTER_REPLICATION_TYPE_SETTING) + List.of(FeatureFlags.SEGMENT_REPLICATION_EXPERIMENTAL), + List.of(IndicesService.CLUSTER_REPLICATION_TYPE_SETTING), + List.of(FeatureFlags.REMOTE_STORE, FeatureFlags.REPLICATION_TYPE), + List.of( + IndicesService.CLUSTER_REMOTE_STORE_ENABLED_SETTING, + IndicesService.CLUSTER_REMOTE_STORE_REPOSITORY_SETTING, + IndicesService.CLUSTER_REMOTE_TRANSLOG_STORE_ENABLED_SETTING, + IndicesService.CLUSTER_REMOTE_TRANSLOG_REPOSITORY_SETTING + ) ); - } diff --git a/server/src/main/java/org/opensearch/common/settings/SettingsModule.java b/server/src/main/java/org/opensearch/common/settings/SettingsModule.java index ab91e2ef185b3..dd5502054684a 100644 --- a/server/src/main/java/org/opensearch/common/settings/SettingsModule.java +++ b/server/src/main/java/org/opensearch/common/settings/SettingsModule.java @@ -91,8 +91,8 @@ public SettingsModule( registerSetting(setting); } - for (Map.Entry> featureFlaggedSetting : ClusterSettings.FEATURE_FLAGGED_CLUSTER_SETTINGS.entrySet()) { - if (FeatureFlags.isEnabled(featureFlaggedSetting.getKey())) { + for (Map.Entry, List> featureFlaggedSetting : ClusterSettings.FEATURE_FLAGGED_CLUSTER_SETTINGS.entrySet()) { + if (featureFlaggedSetting.getKey().stream().allMatch(FeatureFlags::isEnabled)) { featureFlaggedSetting.getValue().forEach(this::registerSetting); } } diff --git a/server/src/main/java/org/opensearch/indices/IndicesService.java b/server/src/main/java/org/opensearch/indices/IndicesService.java index cfa68858e49fe..fc2ee43290862 100644 --- a/server/src/main/java/org/opensearch/indices/IndicesService.java +++ b/server/src/main/java/org/opensearch/indices/IndicesService.java @@ -244,6 +244,46 @@ public class IndicesService extends AbstractLifecycleComponent Property.Final ); + /** + * Used to specify if all indexes are to create with remote store enabled by default + */ + public static final Setting CLUSTER_REMOTE_STORE_ENABLED_SETTING = Setting.boolSetting( + "cluster.remote_store.enabled", + false, + Property.NodeScope, + Property.Final + ); + + /** + * Used to specify default repo to use for segment upload for remote store backed indices + */ + public static final Setting CLUSTER_REMOTE_STORE_REPOSITORY_SETTING = Setting.simpleString( + "cluster.remote_store.repository", + "", + Property.NodeScope, + Property.Final + ); + + /** + * Used to specify if all indexes are to create with translog remote store enabled by default + */ + public static final Setting CLUSTER_REMOTE_TRANSLOG_STORE_ENABLED_SETTING = Setting.boolSetting( + "cluster.remote_store.translog.enabled", + false, + Property.NodeScope, + Property.Final + ); + + /** + * Used to specify default repo to use for translog upload for remote store backed indices + */ + public static final Setting CLUSTER_REMOTE_TRANSLOG_REPOSITORY_SETTING = Setting.simpleString( + "cluster.remote_store.translog.repository", + "", + Property.NodeScope, + Property.Final + ); + /** * The node's settings. */ diff --git a/server/src/test/java/org/opensearch/cluster/metadata/MetadataCreateIndexServiceTests.java b/server/src/test/java/org/opensearch/cluster/metadata/MetadataCreateIndexServiceTests.java index 9b3eeff1ffe1a..a8e3b0b0f277a 100644 --- a/server/src/test/java/org/opensearch/cluster/metadata/MetadataCreateIndexServiceTests.java +++ b/server/src/test/java/org/opensearch/cluster/metadata/MetadataCreateIndexServiceTests.java @@ -65,6 +65,7 @@ import org.opensearch.common.settings.Settings; import org.opensearch.common.unit.TimeValue; import org.opensearch.common.util.BigArrays; +import org.opensearch.common.util.FeatureFlags; import org.opensearch.core.xcontent.NamedXContentRegistry; import org.opensearch.common.xcontent.XContentFactory; import org.opensearch.env.Environment; @@ -79,8 +80,10 @@ import org.opensearch.indices.ShardLimitValidator; import org.opensearch.indices.SystemIndexDescriptor; import org.opensearch.indices.SystemIndices; +import org.opensearch.indices.replication.common.ReplicationType; import org.opensearch.snapshots.EmptySnapshotsInfoService; import org.opensearch.test.ClusterServiceUtils; +import org.opensearch.test.FeatureFlagSetter; import org.opensearch.test.OpenSearchTestCase; import org.opensearch.test.VersionUtils; import org.opensearch.test.gateway.TestGatewayAllocator; @@ -110,6 +113,7 @@ import static java.util.Collections.emptyMap; import static java.util.Collections.singleton; import static java.util.Collections.singletonList; +import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.endsWith; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasKey; @@ -126,6 +130,12 @@ import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_REPLICAS; import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_NUMBER_OF_SHARDS; import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_READ_ONLY; +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_REMOTE_STORE_ENABLED; +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_REMOTE_STORE_REPOSITORY; +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_REMOTE_TRANSLOG_BUFFER_INTERVAL; +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_REMOTE_TRANSLOG_STORE_ENABLED; +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_REMOTE_TRANSLOG_STORE_REPOSITORY; +import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_REPLICATION_TYPE; import static org.opensearch.cluster.metadata.IndexMetadata.SETTING_VERSION_CREATED; import static org.opensearch.cluster.metadata.MetadataCreateIndexService.aggregateIndexSettings; import static org.opensearch.cluster.metadata.MetadataCreateIndexService.buildIndexMetadata; @@ -134,6 +144,11 @@ import static org.opensearch.cluster.metadata.MetadataCreateIndexService.parseV1Mappings; import static org.opensearch.cluster.metadata.MetadataCreateIndexService.resolveAndValidateAliases; import static org.opensearch.index.IndexSettings.INDEX_SOFT_DELETES_SETTING; +import static org.opensearch.indices.IndicesService.CLUSTER_REMOTE_STORE_REPOSITORY_SETTING; +import static org.opensearch.indices.IndicesService.CLUSTER_REMOTE_TRANSLOG_REPOSITORY_SETTING; +import static org.opensearch.indices.IndicesService.CLUSTER_REMOTE_STORE_ENABLED_SETTING; +import static org.opensearch.indices.IndicesService.CLUSTER_REMOTE_TRANSLOG_STORE_ENABLED_SETTING; +import static org.opensearch.indices.IndicesService.CLUSTER_REPLICATION_TYPE_SETTING; import static org.opensearch.indices.ShardLimitValidatorTests.createTestShardLimitService; public class MetadataCreateIndexServiceTests extends OpenSearchTestCase { @@ -1119,6 +1134,260 @@ public void testvalidateIndexSettings() { threadPool.shutdown(); } + public void testRemoteStoreNoUserOverrideConflictingReplicationTypeIndexSettings() { + Settings settings = Settings.builder() + .put(CLUSTER_REPLICATION_TYPE_SETTING.getKey(), ReplicationType.DOCUMENT) + .put(CLUSTER_REMOTE_STORE_ENABLED_SETTING.getKey(), true) + .put(CLUSTER_REMOTE_STORE_REPOSITORY_SETTING.getKey(), "my-segment-repo-1") + .put(CLUSTER_REMOTE_TRANSLOG_STORE_ENABLED_SETTING.getKey(), true) + .put(CLUSTER_REMOTE_TRANSLOG_REPOSITORY_SETTING.getKey(), "my-translog-repo-1") + .build(); + FeatureFlagSetter.set(FeatureFlags.REMOTE_STORE); + FeatureFlagSetter.set(FeatureFlags.REPLICATION_TYPE); + + request = new CreateIndexClusterStateUpdateRequest("create index", "test", "test"); + IllegalArgumentException exc = expectThrows( + IllegalArgumentException.class, + () -> aggregateIndexSettings( + ClusterState.EMPTY_STATE, + request, + Settings.EMPTY, + null, + settings, + IndexScopedSettings.DEFAULT_SCOPED_SETTINGS, + randomShardLimitService(), + Collections.emptySet() + ) + ); + assertThat( + exc.getMessage(), + containsString("Cannot enable [index.remote_store.enabled] when [cluster.indices.replication.strategy] is DOCUMENT") + ); + } + + public void testRemoteStoreNoUserOverrideExceptReplicationTypeSegmentIndexSettings() { + Settings settings = Settings.builder() + .put(CLUSTER_REPLICATION_TYPE_SETTING.getKey(), ReplicationType.DOCUMENT) + .put(CLUSTER_REMOTE_STORE_ENABLED_SETTING.getKey(), true) + .put(CLUSTER_REMOTE_STORE_REPOSITORY_SETTING.getKey(), "my-segment-repo-1") + .put(CLUSTER_REMOTE_TRANSLOG_STORE_ENABLED_SETTING.getKey(), true) + .put(CLUSTER_REMOTE_TRANSLOG_REPOSITORY_SETTING.getKey(), "my-translog-repo-1") + .build(); + FeatureFlagSetter.set(FeatureFlags.REMOTE_STORE); + FeatureFlagSetter.set(FeatureFlags.REPLICATION_TYPE); + + request = new CreateIndexClusterStateUpdateRequest("create index", "test", "test"); + final Settings.Builder requestSettings = Settings.builder(); + requestSettings.put(SETTING_REPLICATION_TYPE, ReplicationType.SEGMENT); + request.settings(requestSettings.build()); + Settings indexSettings = aggregateIndexSettings( + ClusterState.EMPTY_STATE, + request, + Settings.EMPTY, + null, + settings, + IndexScopedSettings.DEFAULT_SCOPED_SETTINGS, + randomShardLimitService(), + Collections.emptySet() + ); + verifyRemoteStoreIndexSettings( + indexSettings, + "true", + "my-segment-repo-1", + "true", + "my-translog-repo-1", + ReplicationType.SEGMENT.toString(), + null + ); + } + + public void testRemoteStoreNoUserOverrideIndexSettings() { + Settings settings = Settings.builder() + .put(CLUSTER_REPLICATION_TYPE_SETTING.getKey(), ReplicationType.SEGMENT) + .put(CLUSTER_REMOTE_STORE_ENABLED_SETTING.getKey(), true) + .put(CLUSTER_REMOTE_STORE_REPOSITORY_SETTING.getKey(), "my-segment-repo-1") + .put(CLUSTER_REMOTE_TRANSLOG_STORE_ENABLED_SETTING.getKey(), true) + .put(CLUSTER_REMOTE_TRANSLOG_REPOSITORY_SETTING.getKey(), "my-translog-repo-1") + .build(); + FeatureFlagSetter.set(FeatureFlags.REMOTE_STORE); + FeatureFlagSetter.set(FeatureFlags.REPLICATION_TYPE); + + request = new CreateIndexClusterStateUpdateRequest("create index", "test", "test"); + Settings indexSettings = aggregateIndexSettings( + ClusterState.EMPTY_STATE, + request, + Settings.EMPTY, + null, + settings, + IndexScopedSettings.DEFAULT_SCOPED_SETTINGS, + randomShardLimitService(), + Collections.emptySet() + ); + verifyRemoteStoreIndexSettings( + indexSettings, + "true", + "my-segment-repo-1", + "true", + "my-translog-repo-1", + ReplicationType.SEGMENT.toString(), + null + ); + } + + public void testRemoteStoreDisabledByUserIndexSettings() { + Settings settings = Settings.builder() + .put(CLUSTER_REPLICATION_TYPE_SETTING.getKey(), ReplicationType.SEGMENT) + .put(CLUSTER_REMOTE_STORE_ENABLED_SETTING.getKey(), true) + .put(CLUSTER_REMOTE_STORE_REPOSITORY_SETTING.getKey(), "my-segment-repo-1") + .put(CLUSTER_REMOTE_TRANSLOG_REPOSITORY_SETTING.getKey(), "my-translog-repo-1") + .build(); + FeatureFlagSetter.set(FeatureFlags.REMOTE_STORE); + FeatureFlagSetter.set(FeatureFlags.REPLICATION_TYPE); + + request = new CreateIndexClusterStateUpdateRequest("create index", "test", "test"); + final Settings.Builder requestSettings = Settings.builder(); + requestSettings.put(SETTING_REMOTE_STORE_ENABLED, false); + request.settings(requestSettings.build()); + Settings indexSettings = aggregateIndexSettings( + ClusterState.EMPTY_STATE, + request, + Settings.EMPTY, + null, + settings, + IndexScopedSettings.DEFAULT_SCOPED_SETTINGS, + randomShardLimitService(), + Collections.emptySet() + ); + verifyRemoteStoreIndexSettings(indexSettings, "false", null, null, null, null, null); + } + + public void testRemoteStoreTranslogDisabledByUserIndexSettings() { + Settings settings = Settings.builder() + .put(CLUSTER_REPLICATION_TYPE_SETTING.getKey(), ReplicationType.SEGMENT) + .put(CLUSTER_REMOTE_STORE_ENABLED_SETTING.getKey(), true) + .put(CLUSTER_REMOTE_STORE_REPOSITORY_SETTING.getKey(), "my-segment-repo-1") + .put(CLUSTER_REMOTE_TRANSLOG_REPOSITORY_SETTING.getKey(), "my-translog-repo-1") + .build(); + FeatureFlagSetter.set(FeatureFlags.REMOTE_STORE); + FeatureFlagSetter.set(FeatureFlags.REPLICATION_TYPE); + + request = new CreateIndexClusterStateUpdateRequest("create index", "test", "test"); + final Settings.Builder requestSettings = Settings.builder(); + requestSettings.put(SETTING_REMOTE_TRANSLOG_STORE_ENABLED, false); + request.settings(requestSettings.build()); + Settings indexSettings = aggregateIndexSettings( + ClusterState.EMPTY_STATE, + request, + Settings.EMPTY, + null, + settings, + IndexScopedSettings.DEFAULT_SCOPED_SETTINGS, + randomShardLimitService(), + Collections.emptySet() + ); + verifyRemoteStoreIndexSettings(indexSettings, "true", "my-segment-repo-1", "false", null, ReplicationType.SEGMENT.toString(), null); + } + + public void testRemoteStoreOverrideSegmentRepoIndexSettings() { + Settings settings = Settings.builder() + .put(CLUSTER_REPLICATION_TYPE_SETTING.getKey(), ReplicationType.SEGMENT) + .put(CLUSTER_REMOTE_STORE_ENABLED_SETTING.getKey(), true) + .put(CLUSTER_REMOTE_STORE_REPOSITORY_SETTING.getKey(), "my-segment-repo-1") + .put(CLUSTER_REMOTE_TRANSLOG_STORE_ENABLED_SETTING.getKey(), true) + .put(CLUSTER_REMOTE_TRANSLOG_REPOSITORY_SETTING.getKey(), "my-translog-repo-1") + .build(); + FeatureFlagSetter.set(FeatureFlags.REMOTE_STORE); + FeatureFlagSetter.set(FeatureFlags.REPLICATION_TYPE); + + request = new CreateIndexClusterStateUpdateRequest("create index", "test", "test"); + final Settings.Builder requestSettings = Settings.builder(); + requestSettings.put(SETTING_REPLICATION_TYPE, ReplicationType.SEGMENT) + .put(SETTING_REMOTE_STORE_ENABLED, true) + .put(SETTING_REMOTE_STORE_REPOSITORY, "my-custom-repo"); + request.settings(requestSettings.build()); + Settings indexSettings = aggregateIndexSettings( + ClusterState.EMPTY_STATE, + request, + Settings.EMPTY, + null, + settings, + IndexScopedSettings.DEFAULT_SCOPED_SETTINGS, + randomShardLimitService(), + Collections.emptySet() + ); + verifyRemoteStoreIndexSettings( + indexSettings, + "true", + "my-custom-repo", + "true", + "my-translog-repo-1", + ReplicationType.SEGMENT.toString(), + null + ); + } + + public void testRemoteStoreOverrideTranslogRepoIndexSettings() { + Settings settings = Settings.builder() + .put(CLUSTER_REPLICATION_TYPE_SETTING.getKey(), ReplicationType.SEGMENT) + .put(CLUSTER_REMOTE_STORE_ENABLED_SETTING.getKey(), true) + .put(CLUSTER_REMOTE_STORE_REPOSITORY_SETTING.getKey(), "my-segment-repo-1") + .put(CLUSTER_REMOTE_TRANSLOG_REPOSITORY_SETTING.getKey(), "my-translog-repo-1") + .build(); + FeatureFlagSetter.set(FeatureFlags.REMOTE_STORE); + FeatureFlagSetter.set(FeatureFlags.REPLICATION_TYPE); + + request = new CreateIndexClusterStateUpdateRequest("create index", "test", "test"); + final Settings.Builder requestSettings = Settings.builder(); + requestSettings.put(SETTING_REMOTE_TRANSLOG_STORE_REPOSITORY, "my-custom-repo").put(SETTING_REMOTE_TRANSLOG_STORE_ENABLED, true); + request.settings(requestSettings.build()); + Settings indexSettings = aggregateIndexSettings( + ClusterState.EMPTY_STATE, + request, + Settings.EMPTY, + null, + settings, + IndexScopedSettings.DEFAULT_SCOPED_SETTINGS, + randomShardLimitService(), + Collections.emptySet() + ); + verifyRemoteStoreIndexSettings( + indexSettings, + "true", + "my-segment-repo-1", + "true", + "my-custom-repo", + ReplicationType.SEGMENT.toString(), + null + ); + } + + public void testRemoteStoreOverrideReplicationTypeIndexSettings() { + Settings settings = Settings.builder() + .put(CLUSTER_REPLICATION_TYPE_SETTING.getKey(), ReplicationType.SEGMENT) + .put(CLUSTER_REMOTE_STORE_ENABLED_SETTING.getKey(), true) + .put(CLUSTER_REMOTE_STORE_REPOSITORY_SETTING.getKey(), "my-segment-repo-1") + .put(CLUSTER_REMOTE_TRANSLOG_REPOSITORY_SETTING.getKey(), "my-translog-repo-1") + .build(); + FeatureFlagSetter.set(FeatureFlags.REMOTE_STORE); + FeatureFlagSetter.set(FeatureFlags.REPLICATION_TYPE); + + request = new CreateIndexClusterStateUpdateRequest("create index", "test", "test"); + final Settings.Builder requestSettings = Settings.builder(); + requestSettings.put(SETTING_REPLICATION_TYPE, ReplicationType.DOCUMENT); + request.settings(requestSettings.build()); + Settings indexSettings = aggregateIndexSettings( + ClusterState.EMPTY_STATE, + request, + Settings.EMPTY, + null, + settings, + IndexScopedSettings.DEFAULT_SCOPED_SETTINGS, + randomShardLimitService(), + Collections.emptySet() + ); + verifyRemoteStoreIndexSettings(indexSettings, null, null, null, null, ReplicationType.DOCUMENT.toString(), null); + } + public void testBuildIndexMetadata() { IndexMetadata sourceIndexMetadata = IndexMetadata.builder("parent") .settings(Settings.builder().put("index.version.created", Version.CURRENT).build()) @@ -1316,4 +1585,21 @@ private void withTemporaryClusterService(BiConsumer threadPool.shutdown(); } } + + private void verifyRemoteStoreIndexSettings( + Settings indexSettings, + String isRemoteSegmentEnabled, + String remoteSegmentRepo, + String isRemoteTranslogEnabled, + String remoteTranslogRepo, + String replicationType, + String translogBufferInterval + ) { + assertEquals(replicationType, indexSettings.get(SETTING_REPLICATION_TYPE)); + assertEquals(isRemoteSegmentEnabled, indexSettings.get(SETTING_REMOTE_STORE_ENABLED)); + assertEquals(remoteSegmentRepo, indexSettings.get(SETTING_REMOTE_STORE_REPOSITORY)); + assertEquals(isRemoteTranslogEnabled, indexSettings.get(SETTING_REMOTE_TRANSLOG_STORE_ENABLED)); + assertEquals(remoteTranslogRepo, indexSettings.get(SETTING_REMOTE_TRANSLOG_STORE_REPOSITORY)); + assertEquals(translogBufferInterval, indexSettings.get(SETTING_REMOTE_TRANSLOG_BUFFER_INTERVAL)); + } } diff --git a/server/src/test/java/org/opensearch/index/IndexSettingsTests.java b/server/src/test/java/org/opensearch/index/IndexSettingsTests.java index 5c9f76da52b31..3d4d505147a04 100644 --- a/server/src/test/java/org/opensearch/index/IndexSettingsTests.java +++ b/server/src/test/java/org/opensearch/index/IndexSettingsTests.java @@ -855,7 +855,7 @@ public void testEnablingRemoteTranslogStoreFailsWhenRemoteSegmentDisabled() { () -> IndexMetadata.INDEX_REMOTE_TRANSLOG_STORE_ENABLED_SETTING.get(indexSettings) ); assertEquals( - "Settings index.remote_store.translog.enabled can ont be set/enabled when index.remote_store.enabled is set to true", + "Settings index.remote_store.translog.enabled can only be set/enabled when index.remote_store.enabled is set to true", iae.getMessage() ); } @@ -930,7 +930,7 @@ public void testSetRemoteRepositoryFailsWhenRemoteStoreIsNotEnabled() { () -> IndexMetadata.INDEX_REMOTE_STORE_REPOSITORY_SETTING.get(indexSettings) ); assertEquals( - "Settings index.remote_store.repository can ont be set/enabled when index.remote_store.enabled is set to true", + "Settings index.remote_store.repository can only be set/enabled when index.remote_store.enabled is set to true", iae.getMessage() ); }