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 03784df509ed6..80b78cfe154f1 100644
--- a/server/src/main/java/org/opensearch/cluster/metadata/IndexMetadata.java
+++ b/server/src/main/java/org/opensearch/cluster/metadata/IndexMetadata.java
@@ -635,6 +635,7 @@ public static APIBlock readFrom(StreamInput input) throws IOException {
static final String KEY_ROLLOVER_INFOS = "rollover_info";
static final String KEY_SYSTEM = "system";
public static final String KEY_PRIMARY_TERMS = "primary_terms";
+ public static final String REMOTE_STORE_CUSTOM_KEY = "remote_store";
public static final String INDEX_STATE_FILE_PREFIX = "state-";
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 acc2f3a294745..f117d1a4a11a2 100644
--- a/server/src/main/java/org/opensearch/cluster/metadata/MetadataCreateIndexService.java
+++ b/server/src/main/java/org/opensearch/cluster/metadata/MetadataCreateIndexService.java
@@ -88,6 +88,8 @@
import org.opensearch.index.mapper.MapperService;
import org.opensearch.index.mapper.MapperService.MergeReason;
import org.opensearch.index.query.QueryShardContext;
+import org.opensearch.index.remote.RemoteStorePathResolver;
+import org.opensearch.index.remote.RemoteStorePathType;
import org.opensearch.index.shard.IndexSettingProvider;
import org.opensearch.index.translog.Translog;
import org.opensearch.indices.IndexCreationException;
@@ -167,6 +169,9 @@ public class MetadataCreateIndexService {
private final ClusterManagerTaskThrottler.ThrottlingKey createIndexTaskKey;
private AwarenessReplicaBalance awarenessReplicaBalance;
+ @Nullable
+ private final RemoteStorePathResolver remoteStorePathResolver;
+
public MetadataCreateIndexService(
final Settings settings,
final ClusterService clusterService,
@@ -198,6 +203,9 @@ public MetadataCreateIndexService(
// Task is onboarded for throttling, it will get retried from associated TransportClusterManagerNodeAction.
createIndexTaskKey = clusterService.registerClusterManagerTask(ClusterManagerTaskKeys.CREATE_INDEX_KEY, true);
+ remoteStorePathResolver = isRemoteDataAttributePresent(settings)
+ ? new RemoteStorePathResolver(clusterService.getClusterSettings())
+ : null;
}
/**
@@ -498,7 +506,8 @@ private ClusterState applyCreateIndexWithTemporaryService(
temporaryIndexMeta.getSettings(),
temporaryIndexMeta.getRoutingNumShards(),
sourceMetadata,
- temporaryIndexMeta.isSystem()
+ temporaryIndexMeta.isSystem(),
+ temporaryIndexMeta.getCustomData()
);
} catch (Exception e) {
logger.info("failed to build index metadata [{}]", request.index());
@@ -522,10 +531,11 @@ private ClusterState applyCreateIndexWithTemporaryService(
/**
* Given a state and index settings calculated after applying templates, validate metadata for
- * the new index, returning an {@link IndexMetadata} for the new index
+ * the new index, returning an {@link IndexMetadata} for the new index.
+ *
+ * The access level of the method changed to default level for visibility to test.
*/
- private IndexMetadata buildAndValidateTemporaryIndexMetadata(
- final ClusterState currentState,
+ IndexMetadata buildAndValidateTemporaryIndexMetadata(
final Settings aggregatedIndexSettings,
final CreateIndexClusterStateUpdateRequest request,
final int routingNumShards
@@ -544,6 +554,11 @@ private IndexMetadata buildAndValidateTemporaryIndexMetadata(
tmpImdBuilder.settings(indexSettings);
tmpImdBuilder.system(isSystem);
+ if (remoteStorePathResolver != null) {
+ String pathType = remoteStorePathResolver.resolveType().toString();
+ tmpImdBuilder.putCustom(IndexMetadata.REMOTE_STORE_CUSTOM_KEY, Map.of(RemoteStorePathType.NAME, pathType));
+ }
+
// Set up everything, now locally create the index to see that things are ok, and apply
IndexMetadata tempMetadata = tmpImdBuilder.build();
validateActiveShardCount(request.waitForActiveShards(), tempMetadata);
@@ -582,7 +597,7 @@ private ClusterState applyCreateIndexRequestWithV1Templates(
clusterService.getClusterSettings()
);
int routingNumShards = getIndexNumberOfRoutingShards(aggregatedIndexSettings, null);
- IndexMetadata tmpImd = buildAndValidateTemporaryIndexMetadata(currentState, aggregatedIndexSettings, request, routingNumShards);
+ IndexMetadata tmpImd = buildAndValidateTemporaryIndexMetadata(aggregatedIndexSettings, request, routingNumShards);
return applyCreateIndexWithTemporaryService(
currentState,
@@ -647,7 +662,7 @@ private ClusterState applyCreateIndexRequestWithV2Template(
clusterService.getClusterSettings()
);
int routingNumShards = getIndexNumberOfRoutingShards(aggregatedIndexSettings, null);
- IndexMetadata tmpImd = buildAndValidateTemporaryIndexMetadata(currentState, aggregatedIndexSettings, request, routingNumShards);
+ IndexMetadata tmpImd = buildAndValidateTemporaryIndexMetadata(aggregatedIndexSettings, request, routingNumShards);
return applyCreateIndexWithTemporaryService(
currentState,
@@ -728,7 +743,7 @@ private ClusterState applyCreateIndexRequestWithExistingMetadata(
clusterService.getClusterSettings()
);
final int routingNumShards = getIndexNumberOfRoutingShards(aggregatedIndexSettings, sourceMetadata);
- IndexMetadata tmpImd = buildAndValidateTemporaryIndexMetadata(currentState, aggregatedIndexSettings, request, routingNumShards);
+ IndexMetadata tmpImd = buildAndValidateTemporaryIndexMetadata(aggregatedIndexSettings, request, routingNumShards);
return applyCreateIndexWithTemporaryService(
currentState,
@@ -1147,7 +1162,8 @@ static IndexMetadata buildIndexMetadata(
Settings indexSettings,
int routingNumShards,
@Nullable IndexMetadata sourceMetadata,
- boolean isSystem
+ boolean isSystem,
+ Map customData
) {
IndexMetadata.Builder indexMetadataBuilder = createIndexMetadataBuilder(indexName, sourceMetadata, indexSettings, routingNumShards);
indexMetadataBuilder.system(isSystem);
@@ -1168,6 +1184,10 @@ static IndexMetadata buildIndexMetadata(
indexMetadataBuilder.putAlias(aliases.get(i));
}
+ for (Map.Entry entry : customData.entrySet()) {
+ indexMetadataBuilder.putCustom(entry.getKey(), entry.getValue());
+ }
+
indexMetadataBuilder.state(IndexMetadata.State.OPEN);
return indexMetadataBuilder.build();
}
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 6b4be45929553..730a330b6ca60 100644
--- a/server/src/main/java/org/opensearch/common/settings/ClusterSettings.java
+++ b/server/src/main/java/org/opensearch/common/settings/ClusterSettings.java
@@ -713,6 +713,8 @@ public void apply(Settings value, Settings current, Settings previous) {
IoBasedAdmissionControllerSettings.SEARCH_IO_USAGE_LIMIT,
IoBasedAdmissionControllerSettings.INDEXING_IO_USAGE_LIMIT,
IndicesService.CLUSTER_INDEX_RESTRICT_REPLICATION_TYPE_SETTING,
+ IndicesService.CLUSTER_REMOTE_STORE_PATH_PREFIX_TYPE_SETTING,
+
// Concurrent segment search settings
SearchService.CLUSTER_CONCURRENT_SEGMENT_SEARCH_SETTING,
SearchService.CONCURRENT_SEGMENT_SEARCH_TARGET_MAX_SLICE_COUNT_SETTING
diff --git a/server/src/main/java/org/opensearch/index/remote/RemoteStorePathResolver.java b/server/src/main/java/org/opensearch/index/remote/RemoteStorePathResolver.java
new file mode 100644
index 0000000000000..6e8126fcce0ca
--- /dev/null
+++ b/server/src/main/java/org/opensearch/index/remote/RemoteStorePathResolver.java
@@ -0,0 +1,30 @@
+/*
+ * 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.index.remote;
+
+import org.opensearch.common.settings.ClusterSettings;
+import org.opensearch.indices.IndicesService;
+
+/**
+ * Determines the {@link RemoteStorePathType} at the time of index metadata creation.
+ *
+ * @opensearch.internal
+ */
+public class RemoteStorePathResolver {
+
+ private final ClusterSettings clusterSettings;
+
+ public RemoteStorePathResolver(ClusterSettings clusterSettings) {
+ this.clusterSettings = clusterSettings;
+ }
+
+ public RemoteStorePathType resolveType() {
+ return clusterSettings.get(IndicesService.CLUSTER_REMOTE_STORE_PATH_PREFIX_TYPE_SETTING);
+ }
+}
diff --git a/server/src/main/java/org/opensearch/index/remote/RemoteStorePathType.java b/server/src/main/java/org/opensearch/index/remote/RemoteStorePathType.java
new file mode 100644
index 0000000000000..a64e07ab1f66f
--- /dev/null
+++ b/server/src/main/java/org/opensearch/index/remote/RemoteStorePathType.java
@@ -0,0 +1,36 @@
+/*
+ * 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.index.remote;
+
+import java.util.Locale;
+
+/**
+ * Enumerates the types of remote store paths resolution techniques supported by OpenSearch.
+ * For more information, see Github issue #12567.
+ *
+ * @opensearch.internal
+ */
+public enum RemoteStorePathType {
+
+ FIXED,
+ HASHED_PREFIX;
+
+ public static RemoteStorePathType parseString(String remoteStoreBlobPathType) {
+ try {
+ return RemoteStorePathType.valueOf(remoteStoreBlobPathType.toUpperCase(Locale.ROOT));
+ } catch (IllegalArgumentException e) {
+ throw new IllegalArgumentException("Could not parse RemoteStorePathType for [" + remoteStoreBlobPathType + "]");
+ }
+ }
+
+ /**
+ * This string is used as key for storing information in the custom data in index settings.
+ */
+ public static final String NAME = "path_type";
+}
diff --git a/server/src/main/java/org/opensearch/indices/IndicesService.java b/server/src/main/java/org/opensearch/indices/IndicesService.java
index 40c10e3a2fe96..0e64894e6f708 100644
--- a/server/src/main/java/org/opensearch/indices/IndicesService.java
+++ b/server/src/main/java/org/opensearch/indices/IndicesService.java
@@ -123,6 +123,7 @@
import org.opensearch.index.query.QueryRewriteContext;
import org.opensearch.index.recovery.RecoveryStats;
import org.opensearch.index.refresh.RefreshStats;
+import org.opensearch.index.remote.RemoteStorePathType;
import org.opensearch.index.remote.RemoteStoreStatsTrackerFactory;
import org.opensearch.index.search.stats.SearchStats;
import org.opensearch.index.seqno.RetentionLeaseStats;
@@ -313,6 +314,18 @@ public class IndicesService extends AbstractLifecycleComponent
Property.Final
);
+ /**
+ * This setting is used to set the remote store blob store path prefix strategy. This setting is effective only for
+ * remote store enabled cluster.
+ */
+ public static final Setting CLUSTER_REMOTE_STORE_PATH_PREFIX_TYPE_SETTING = new Setting<>(
+ "cluster.remote_store.index.path.prefix.type",
+ RemoteStorePathType.FIXED.toString(),
+ RemoteStorePathType::parseString,
+ Property.NodeScope,
+ Property.Dynamic
+ );
+
/**
* 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 cc605878119a2..cf4de32890a2a 100644
--- a/server/src/test/java/org/opensearch/cluster/metadata/MetadataCreateIndexServiceTests.java
+++ b/server/src/test/java/org/opensearch/cluster/metadata/MetadataCreateIndexServiceTests.java
@@ -71,6 +71,7 @@
import org.opensearch.index.IndexSettings;
import org.opensearch.index.mapper.MapperService;
import org.opensearch.index.query.QueryShardContext;
+import org.opensearch.index.remote.RemoteStorePathType;
import org.opensearch.index.translog.Translog;
import org.opensearch.indices.IndexCreationException;
import org.opensearch.indices.IndicesService;
@@ -1563,13 +1564,102 @@ public void testBuildIndexMetadata() {
.put(SETTING_NUMBER_OF_SHARDS, 1)
.build();
List aliases = singletonList(AliasMetadata.builder("alias1").build());
- IndexMetadata indexMetadata = buildIndexMetadata("test", aliases, () -> null, indexSettings, 4, sourceIndexMetadata, false);
+ IndexMetadata indexMetadata = buildIndexMetadata(
+ "test",
+ aliases,
+ () -> null,
+ indexSettings,
+ 4,
+ sourceIndexMetadata,
+ false,
+ new HashMap<>()
+ );
assertThat(indexMetadata.getAliases().size(), is(1));
assertThat(indexMetadata.getAliases().keySet().iterator().next(), is("alias1"));
assertThat("The source index primary term must be used", indexMetadata.primaryTerm(0), is(3L));
}
+ /**
+ * This test checks if the cluster is a remote store cluster then we populate custom data for remote settings in
+ * index metadata of the underlying index. This captures information around the resolution pattern of the path for
+ * remote segments and translog.
+ */
+ public void testRemoteCustomData() {
+ // Case 1 - Remote store is not enabled
+ IndexMetadata indexMetadata = testRemoteCustomData(false, randomFrom(RemoteStorePathType.values()));
+ assertNull(indexMetadata.getCustomData(IndexMetadata.REMOTE_STORE_CUSTOM_KEY));
+
+ // Case 2 - cluster.remote_store.index.path.prefix.optimised=fixed (default value)
+ indexMetadata = testRemoteCustomData(true, RemoteStorePathType.FIXED);
+ validateRemoteCustomData(
+ indexMetadata.getCustomData(IndexMetadata.REMOTE_STORE_CUSTOM_KEY),
+ RemoteStorePathType.NAME,
+ RemoteStorePathType.FIXED.toString()
+ );
+
+ // Case 3 - cluster.remote_store.index.path.prefix.optimised=hashed_prefix
+ indexMetadata = testRemoteCustomData(true, RemoteStorePathType.HASHED_PREFIX);
+ validateRemoteCustomData(
+ indexMetadata.getCustomData(IndexMetadata.REMOTE_STORE_CUSTOM_KEY),
+ RemoteStorePathType.NAME,
+ RemoteStorePathType.HASHED_PREFIX.toString()
+ );
+ }
+
+ private IndexMetadata testRemoteCustomData(boolean remoteStoreEnabled, RemoteStorePathType remoteStorePathType) {
+ Settings.Builder settingsBuilder = Settings.builder();
+ if (remoteStoreEnabled) {
+ settingsBuilder.put(NODE_ATTRIBUTES.getKey() + REMOTE_STORE_SEGMENT_REPOSITORY_NAME_ATTRIBUTE_KEY, "test");
+ }
+ settingsBuilder.put(IndicesService.CLUSTER_REMOTE_STORE_PATH_PREFIX_TYPE_SETTING.getKey(), remoteStorePathType.toString());
+ Settings settings = settingsBuilder.build();
+
+ ClusterService clusterService = mock(ClusterService.class);
+ Metadata metadata = Metadata.builder()
+ .transientSettings(Settings.builder().put(Metadata.DEFAULT_REPLICA_COUNT_SETTING.getKey(), 1).build())
+ .build();
+ ClusterState clusterState = ClusterState.builder(org.opensearch.cluster.ClusterName.CLUSTER_NAME_SETTING.getDefault(Settings.EMPTY))
+ .metadata(metadata)
+ .build();
+ ClusterSettings clusterSettings = new ClusterSettings(settings, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS);
+ when(clusterService.getSettings()).thenReturn(settings);
+ when(clusterService.getClusterSettings()).thenReturn(clusterSettings);
+ when(clusterService.state()).thenReturn(clusterState);
+
+ ThreadPool threadPool = new TestThreadPool(getTestName());
+ MetadataCreateIndexService metadataCreateIndexService = new MetadataCreateIndexService(
+ settings,
+ clusterService,
+ null,
+ null,
+ null,
+ createTestShardLimitService(randomIntBetween(1, 1000), false, clusterService),
+ new Environment(Settings.builder().put("path.home", "dummy").build(), null),
+ IndexScopedSettings.DEFAULT_SCOPED_SETTINGS,
+ threadPool,
+ null,
+ new SystemIndices(Collections.emptyMap()),
+ true,
+ new AwarenessReplicaBalance(settings, clusterService.getClusterSettings())
+ );
+ CreateIndexClusterStateUpdateRequest request = new CreateIndexClusterStateUpdateRequest("create index", "test", "test");
+ Settings indexSettings = Settings.builder()
+ .put("index.version.created", Version.CURRENT)
+ .put(INDEX_NUMBER_OF_SHARDS_SETTING.getKey(), 3)
+ .put(IndexMetadata.SETTING_NUMBER_OF_REPLICAS, 1)
+ .build();
+
+ IndexMetadata indexMetadata = metadataCreateIndexService.buildAndValidateTemporaryIndexMetadata(indexSettings, request, 0);
+ threadPool.shutdown();
+ return indexMetadata;
+ }
+
+ private void validateRemoteCustomData(Map customData, String expectedKey, String expectedValue) {
+ assertTrue(customData.containsKey(expectedKey));
+ assertEquals(expectedValue, customData.get(expectedKey));
+ }
+
public void testGetIndexNumberOfRoutingShardsWithNullSourceIndex() {
Settings indexSettings = Settings.builder()
.put("index.version.created", Version.CURRENT)