-
Notifications
You must be signed in to change notification settings - Fork 2.3k
[merged segment warmer] support local merged segment warmer #18255
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
Merged
ashking94
merged 28 commits into
opensearch-project:main
from
guojialiang92:dev/support-local-merged-segment-warmer
Jun 26, 2025
Merged
Changes from 3 commits
Commits
Show all changes
28 commits
Select commit
Hold shift + click to select a range
aa13d96
support local merged segment warmer
guojialiang92 1c1f802
update log
guojialiang92 58528e9
fix test
guojialiang92 7c8449e
add tests
guojialiang92 e327372
IndexShard support getActiveReplicaNodes
guojialiang92 290c5b9
add test
guojialiang92 f356225
Merge remote-tracking branch 'origin/main' into dev/support-local-mer…
guojialiang92 ddb4258
add pre-verification.
guojialiang92 d63a676
extend ReplicationAction instead of ActionRequest
guojialiang92 c8625bb
reuse Store#getSegmentMetadataMap
guojialiang92 80e5372
refactor updateReplicationRateLimiter
guojialiang92 73d6616
extract an abstract base class (AbstractSegmentReplicationTarget) fro…
guojialiang92 b04b73b
reduce unnecessary exception judgment
guojialiang92 5d18ed1
fix UT
guojialiang92 779b532
add PublishMergedSegmentActionTests
guojialiang92 897d0bb
gradle spotlessApply
guojialiang92 f04152d
add test
guojialiang92 61ba203
Merge remote-tracking branch 'origin/main' into dev/support-local-mer…
guojialiang92 1d83140
Merge remote-tracking branch 'origin/main' into dev/support-local-mer…
guojialiang92 9f5f4c0
rename ReplicationSegmentCheckpoint to MergeSegmentCheckpoint
guojialiang92 01839ab
add some description
guojialiang92 d558c85
refactor code
guojialiang92 96fc56c
extract an abstract base class (AbstractPublishCheckpointAction) from…
guojialiang92 a0b4e3a
fix :server:japicmp
guojialiang92 cc5ea0b
Merge remote-tracking branch 'origin/main' into dev/support-local-mer…
guojialiang92 dedad5e
update
guojialiang92 3224f35
update
guojialiang92 f5111ec
Merge remote-tracking branch 'origin/main' into dev/support-local-mer…
guojialiang92 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
199 changes: 199 additions & 0 deletions
199
...rc/internalClusterTest/java/org/opensearch/indices/replication/MergedSegmentWarmerIT.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,199 @@ | ||
| /* | ||
| * 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.indices.replication; | ||
|
|
||
| import org.apache.logging.log4j.Logger; | ||
| import org.opensearch.action.admin.indices.forcemerge.ForceMergeRequest; | ||
| import org.opensearch.action.admin.indices.segments.IndexShardSegments; | ||
| import org.opensearch.action.admin.indices.segments.IndicesSegmentResponse; | ||
| import org.opensearch.action.admin.indices.segments.ShardSegments; | ||
| import org.opensearch.action.support.WriteRequest; | ||
| import org.opensearch.common.settings.Settings; | ||
| import org.opensearch.common.util.FeatureFlags; | ||
| import org.opensearch.common.util.set.Sets; | ||
| import org.opensearch.index.IndexSettings; | ||
| import org.opensearch.index.TieredMergePolicyProvider; | ||
| import org.opensearch.index.engine.Segment; | ||
| import org.opensearch.index.store.StoreFileMetadata; | ||
| import org.opensearch.test.OpenSearchIntegTestCase; | ||
| import org.opensearch.test.transport.MockTransportService; | ||
| import org.opensearch.transport.TransportService; | ||
|
|
||
| import java.util.Set; | ||
| import java.util.concurrent.CountDownLatch; | ||
| import java.util.concurrent.TimeUnit; | ||
| import java.util.concurrent.atomic.AtomicInteger; | ||
| import java.util.stream.Collectors; | ||
|
|
||
| /** | ||
| * This class runs Segment Replication Integ test suite with merged segment warmer enabled. | ||
| */ | ||
| @OpenSearchIntegTestCase.ClusterScope(scope = OpenSearchIntegTestCase.Scope.TEST, numDataNodes = 0) | ||
| public class MergedSegmentWarmerIT extends SegmentReplicationIT { | ||
| @Override | ||
| protected Settings nodeSettings(int nodeOrdinal) { | ||
| return Settings.builder().put(super.nodeSettings(nodeOrdinal)).build(); | ||
| } | ||
|
|
||
| @Override | ||
| protected Settings featureFlagSettings() { | ||
| Settings.Builder featureSettings = Settings.builder(); | ||
| featureSettings.put(FeatureFlags.MERGED_SEGMENT_WARMER_EXPERIMENTAL_FLAG, true); | ||
| return featureSettings.build(); | ||
| } | ||
|
|
||
| public void testMergeSegmentWarmer() throws Exception { | ||
| final String primaryNode = internalCluster().startDataOnlyNode(); | ||
| final String replicaNode = internalCluster().startDataOnlyNode(); | ||
| createIndex(INDEX_NAME); | ||
| ensureGreen(INDEX_NAME); | ||
|
|
||
| for (int i = 0; i < 30; i++) { | ||
| client().prepareIndex(INDEX_NAME) | ||
| .setId(String.valueOf(i)) | ||
| .setSource("foo" + i, "bar" + i) | ||
| .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE) | ||
| .get(); | ||
| } | ||
|
|
||
| waitForSearchableDocs(30, primaryNode, replicaNode); | ||
|
|
||
| MockTransportService primaryTransportService = ((MockTransportService) internalCluster().getInstance( | ||
| TransportService.class, | ||
| primaryNode | ||
| )); | ||
|
|
||
| primaryTransportService.addRequestHandlingBehavior( | ||
| SegmentReplicationSourceService.Actions.GET_SEGMENT_FILES, | ||
| (handler, request, channel, task) -> { | ||
| logger.info( | ||
| "replicationId {}, get segment files {}", | ||
| ((GetSegmentFilesRequest) request).getReplicationId(), | ||
| ((GetSegmentFilesRequest) request).getFilesToFetch().stream().map(StoreFileMetadata::name).collect(Collectors.toList()) | ||
| ); | ||
| // After the pre-copy merged segment is complete, the merged segment files is to reuse, so the files to fetch is empty. | ||
| assertEquals(0, ((GetSegmentFilesRequest) request).getFilesToFetch().size()); | ||
| handler.messageReceived(request, channel, task); | ||
| } | ||
| ); | ||
|
|
||
| client().admin().indices().forceMerge(new ForceMergeRequest(INDEX_NAME).maxNumSegments(2)); | ||
|
|
||
| waitForSegmentCount(INDEX_NAME, 2, logger); | ||
| primaryTransportService.clearAllRules(); | ||
| } | ||
|
|
||
| public void testConcurrentMergeSegmentWarmer() throws Exception { | ||
| final String primaryNode = internalCluster().startDataOnlyNode(); | ||
| createIndex( | ||
| INDEX_NAME, | ||
| Settings.builder() | ||
| .put(indexSettings()) | ||
| .put(TieredMergePolicyProvider.INDEX_MERGE_POLICY_SEGMENTS_PER_TIER_SETTING.getKey(), 5) | ||
| .put(TieredMergePolicyProvider.INDEX_MERGE_POLICY_MAX_MERGE_AT_ONCE_SETTING.getKey(), 5) | ||
| .put(IndexSettings.INDEX_MERGE_ON_FLUSH_ENABLED.getKey(), false) | ||
| .build() | ||
| ); | ||
| ensureYellowAndNoInitializingShards(INDEX_NAME); | ||
| final String replicaNode = internalCluster().startDataOnlyNode(); | ||
| ensureGreen(INDEX_NAME); | ||
|
|
||
| // ensure pre-copy merge segment concurrent execution | ||
| AtomicInteger getMergeSegmentFilesActionCount = new AtomicInteger(0); | ||
| MockTransportService primaryTransportService = ((MockTransportService) internalCluster().getInstance( | ||
| TransportService.class, | ||
| primaryNode | ||
| )); | ||
|
|
||
| CountDownLatch blockFileCopy = new CountDownLatch(1); | ||
| primaryTransportService.addRequestHandlingBehavior( | ||
| SegmentReplicationSourceService.Actions.GET_MERGED_SEGMENT_FILES, | ||
| (handler, request, channel, task) -> { | ||
| logger.info( | ||
| "replicationId {}, get merge segment files {}", | ||
| ((GetSegmentFilesRequest) request).getReplicationId(), | ||
| ((GetSegmentFilesRequest) request).getFilesToFetch().stream().map(StoreFileMetadata::name).collect(Collectors.toList()) | ||
| ); | ||
| getMergeSegmentFilesActionCount.incrementAndGet(); | ||
| if (getMergeSegmentFilesActionCount.get() > 2) { | ||
| blockFileCopy.countDown(); | ||
| } | ||
| handler.messageReceived(request, channel, task); | ||
| } | ||
| ); | ||
|
|
||
| primaryTransportService.addSendBehavior( | ||
| internalCluster().getInstance(TransportService.class, replicaNode), | ||
| (connection, requestId, action, request, options) -> { | ||
| if (action.equals(SegmentReplicationTargetService.Actions.MERGED_SEGMENT_FILE_CHUNK)) { | ||
| try { | ||
| blockFileCopy.await(); | ||
| } catch (InterruptedException e) { | ||
| throw new RuntimeException(e); | ||
| } | ||
| } | ||
|
|
||
| connection.sendRequest(requestId, action, request, options); | ||
| } | ||
| ); | ||
|
|
||
| for (int i = 0; i < 30; i++) { | ||
| client().prepareIndex(INDEX_NAME) | ||
| .setId(String.valueOf(i)) | ||
| .setSource("foo" + i, "bar" + i) | ||
| .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE) | ||
| .get(); | ||
| } | ||
|
|
||
| client().admin().indices().forceMerge(new ForceMergeRequest(INDEX_NAME).maxNumSegments(2)); | ||
|
|
||
| waitForSegmentCount(INDEX_NAME, 2, logger); | ||
| primaryTransportService.clearAllRules(); | ||
| } | ||
|
|
||
| public void testMergeSegmentWarmerWithInactiveReplica() throws Exception { | ||
| internalCluster().startDataOnlyNode(); | ||
| createIndex(INDEX_NAME); | ||
| ensureYellowAndNoInitializingShards(INDEX_NAME); | ||
|
|
||
| for (int i = 0; i < 30; i++) { | ||
| client().prepareIndex(INDEX_NAME) | ||
| .setId(String.valueOf(i)) | ||
| .setSource("foo" + i, "bar" + i) | ||
| .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE) | ||
| .get(); | ||
| } | ||
|
|
||
| client().admin().indices().forceMerge(new ForceMergeRequest(INDEX_NAME).maxNumSegments(1)).get(); | ||
| final IndicesSegmentResponse response = client().admin().indices().prepareSegments(INDEX_NAME).get(); | ||
| assertEquals(1, response.getIndices().get(INDEX_NAME).getShards().values().size()); | ||
| } | ||
|
|
||
| public static void waitForSegmentCount(String indexName, int segmentCount, Logger logger) throws Exception { | ||
| assertBusy(() -> { | ||
| Set<String> primarySegments = Sets.newHashSet(); | ||
| Set<String> replicaSegments = Sets.newHashSet(); | ||
| final IndicesSegmentResponse response = client().admin().indices().prepareSegments(indexName).get(); | ||
| for (IndexShardSegments indexShardSegments : response.getIndices().get(indexName).getShards().values()) { | ||
| for (ShardSegments shardSegment : indexShardSegments.getShards()) { | ||
| for (Segment segment : shardSegment.getSegments()) { | ||
| if (shardSegment.getShardRouting().primary()) { | ||
| primarySegments.add(segment.getName()); | ||
| } else { | ||
| replicaSegments.add(segment.getName()); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| logger.info("primary segments: {}, replica segments: {}", primarySegments, replicaSegments); | ||
| assertEquals(segmentCount, primarySegments.size()); | ||
| assertEquals(segmentCount, replicaSegments.size()); | ||
| }, 1, TimeUnit.MINUTES); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.