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

Introduce remote store path type in customData in IndexMetadata #12607

Merged
merged 5 commits into from
Mar 19, 2024
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 @@
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 @@

// 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 @@
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 @@

/**
* 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 @@
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 @@
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 @@
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 @@
clusterService.getClusterSettings()
);
final int routingNumShards = getIndexNumberOfRoutingShards(aggregatedIndexSettings, sourceMetadata);
IndexMetadata tmpImd = buildAndValidateTemporaryIndexMetadata(currentState, aggregatedIndexSettings, request, routingNumShards);
IndexMetadata tmpImd = buildAndValidateTemporaryIndexMetadata(aggregatedIndexSettings, request, routingNumShards);

Check warning on line 746 in server/src/main/java/org/opensearch/cluster/metadata/MetadataCreateIndexService.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/org/opensearch/cluster/metadata/MetadataCreateIndexService.java#L746

Added line #L746 was not covered by tests

return applyCreateIndexWithTemporaryService(
currentState,
Expand Down Expand Up @@ -1147,7 +1162,8 @@
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 @@
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 + "]");

Check warning on line 28 in server/src/main/java/org/opensearch/index/remote/RemoteStorePathType.java

View check run for this annotation

Codecov / codecov/patch

server/src/main/java/org/opensearch/index/remote/RemoteStorePathType.java#L27-L28

Added lines #L27 - L28 were not covered by tests
}
}

/**
* 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
ashking94 marked this conversation as resolved.
Show resolved Hide 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
Loading