Skip to content
Merged
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 @@ -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-";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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;
}

/**
Expand Down Expand Up @@ -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());
Expand All @@ -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.
* <p>
* 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
Expand All @@ -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);
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -1147,7 +1162,8 @@ static IndexMetadata buildIndexMetadata(
Settings indexSettings,
int routingNumShards,
@Nullable IndexMetadata sourceMetadata,
boolean isSystem
boolean isSystem,
Map<String, DiffableStringMap> customData
) {
IndexMetadata.Builder indexMetadataBuilder = createIndexMetadataBuilder(indexName, sourceMetadata, indexSettings, routingNumShards);
indexMetadataBuilder.system(isSystem);
Expand All @@ -1168,6 +1184,10 @@ static IndexMetadata buildIndexMetadata(
indexMetadataBuilder.putAlias(aliases.get(i));
}

for (Map.Entry<String, DiffableStringMap> entry : customData.entrySet()) {
indexMetadataBuilder.putCustom(entry.getKey(), entry.getValue());
}

indexMetadataBuilder.state(IndexMetadata.State.OPEN);
return indexMetadataBuilder.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
@@ -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 <a href="https://github.com/opensearch-project/OpenSearch/issues/12567">Github issue #12567</a>.
*
* @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";
}
13 changes: 13 additions & 0 deletions server/src/main/java/org/opensearch/indices/IndicesService.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<RemoteStorePathType> 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
Comment thread
ashking94 marked this conversation as resolved.
);

/**
* The node's settings.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -1563,13 +1564,102 @@ public void testBuildIndexMetadata() {
.put(SETTING_NUMBER_OF_SHARDS, 1)
.build();
List<AliasMetadata> 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<String, String> 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)
Expand Down