Skip to content

Commit 389b54f

Browse files
committed
Implemented index and cluster force merge rate limiting with index-level precedence
1 parent dbd7422 commit 389b54f

File tree

5 files changed

+84
-0
lines changed

5 files changed

+84
-0
lines changed

server/src/main/java/org/opensearch/common/settings/ClusterSettings.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@
111111
import org.opensearch.http.HttpTransportSettings;
112112
import org.opensearch.index.IndexModule;
113113
import org.opensearch.index.IndexSettings;
114+
import org.opensearch.index.MergeSchedulerConfig;
114115
import org.opensearch.index.IndexingPressure;
115116
import org.opensearch.index.SegmentReplicationPressureService;
116117
import org.opensearch.index.ShardIndexingPressureMemoryManager;
@@ -514,6 +515,7 @@ public void apply(Settings value, Settings current, Settings previous) {
514515
IndexSettings.QUERY_STRING_ANALYZE_WILDCARD,
515516
IndexSettings.QUERY_STRING_ALLOW_LEADING_WILDCARD,
516517
IndexSettings.TIME_SERIES_INDEX_MERGE_POLICY,
518+
MergeSchedulerConfig.CLUSTER_MAX_FORCED_MERGE_MB_PER_SEC_SETTING,
517519
ScriptService.SCRIPT_GENERAL_CACHE_SIZE_SETTING,
518520
ScriptService.SCRIPT_GENERAL_CACHE_EXPIRE_SETTING,
519521
ScriptService.SCRIPT_GENERAL_MAX_COMPILATIONS_RATE_SETTING,

server/src/main/java/org/opensearch/common/settings/IndexScopedSettings.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ public final class IndexScopedSettings extends AbstractScopedSettings {
9090
MergeSchedulerConfig.AUTO_THROTTLE_SETTING,
9191
MergeSchedulerConfig.MAX_MERGE_COUNT_SETTING,
9292
MergeSchedulerConfig.MAX_THREAD_COUNT_SETTING,
93+
MergeSchedulerConfig.MAX_FORCED_MERGE_MB_PER_SEC_SETTING,
9394
IndexMetadata.SETTING_INDEX_VERSION_CREATED,
9495
IndexMetadata.SETTING_INDEX_CREATION_DATE,
9596
IndexMetadata.INDEX_UUID_SETTING,

server/src/main/java/org/opensearch/index/IndexSettings.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1178,6 +1178,7 @@ public IndexSettings(final IndexMetadata indexMetadata, final Settings nodeSetti
11781178
mergeSchedulerConfig::setMaxThreadAndMergeCount
11791179
);
11801180
scopedSettings.addSettingsUpdateConsumer(MergeSchedulerConfig.AUTO_THROTTLE_SETTING, mergeSchedulerConfig::setAutoThrottle);
1181+
scopedSettings.addSettingsUpdateConsumer(MergeSchedulerConfig.MAX_FORCED_MERGE_MB_PER_SEC_SETTING, mergeSchedulerConfig::setMaxForcedMergeMBPerSec);
11811182
scopedSettings.addSettingsUpdateConsumer(INDEX_TRANSLOG_DURABILITY_SETTING, this::setTranslogDurability);
11821183
scopedSettings.addSettingsUpdateConsumer(INDEX_TRANSLOG_SYNC_INTERVAL_SETTING, this::setTranslogSyncInterval);
11831184
scopedSettings.addSettingsUpdateConsumer(

server/src/main/java/org/opensearch/index/MergeSchedulerConfig.java

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,28 @@
6363
* unluckily suddenly requires a large merge will see that merge aggressively
6464
* throttled, while an application doing heavy indexing will see the throttle
6565
* move higher to allow merges to keep up with ongoing indexing.
66+
*
67+
* <li><code>index.merge.scheduler.max_forced_merge_mb_per_sec</code>:
68+
* <p>
69+
* Controls the rate limiting for forced merges in MB per second at the index level.
70+
* The default value of Double.POSITIVE_INFINITY means no rate limiting is applied.
71+
* Setting a finite positive value will limit the throughput of forced merge operations
72+
* to the specified rate. This setting takes precedence over the cluster-level setting.
73+
*
74+
* <li><code>cluster.merge.scheduler.max_forced_merge_mb_per_sec</code>:
75+
* <p>
76+
* Controls the rate limiting for forced merges in MB per second at the cluster level.
77+
* The default value of Double.POSITIVE_INFINITY means no rate limiting is applied.
78+
* This setting is used as a fallback when the index-level setting is not specified.
79+
* Index-level settings take precedence over this cluster-level setting.
80+
* </ul>
81+
*
82+
* <p><b>Setting Precedence:</b>
83+
* <ul>
84+
* <li>If only index-level setting is specified: uses index value
85+
* <li>If only cluster-level setting is specified: uses cluster value
86+
* <li>If both settings are specified: uses index value (index takes precedence)
87+
* <li>If neither setting is specified: uses default value (Double.POSITIVE_INFINITY)
6688
* </ul>
6789
*
6890
* @opensearch.api
@@ -90,16 +112,41 @@ public final class MergeSchedulerConfig {
90112
Property.Dynamic,
91113
Property.IndexScope
92114
);
115+
public static final Setting<Double> MAX_FORCED_MERGE_MB_PER_SEC_SETTING = Setting.doubleSetting(
116+
"index.merge.scheduler.max_forced_merge_mb_per_sec",
117+
Double.POSITIVE_INFINITY,
118+
0.0d,
119+
Double.POSITIVE_INFINITY,
120+
Property.Dynamic,
121+
Property.IndexScope
122+
);
123+
124+
public static final Setting<Double> CLUSTER_MAX_FORCED_MERGE_MB_PER_SEC_SETTING = Setting.doubleSetting(
125+
"cluster.merge.scheduler.max_forced_merge_mb_per_sec",
126+
Double.POSITIVE_INFINITY,
127+
0.0d,
128+
Double.POSITIVE_INFINITY,
129+
Property.Dynamic,
130+
Property.NodeScope
131+
);
93132

94133
private volatile boolean autoThrottle;
95134
private volatile int maxThreadCount;
96135
private volatile int maxMergeCount;
136+
private volatile double maxForcedMergeMBPerSec;
97137

98138
MergeSchedulerConfig(IndexSettings indexSettings) {
99139
int maxThread = indexSettings.getValue(MAX_THREAD_COUNT_SETTING);
100140
int maxMerge = indexSettings.getValue(MAX_MERGE_COUNT_SETTING);
101141
setMaxThreadAndMergeCount(maxThread, maxMerge);
102142
this.autoThrottle = indexSettings.getValue(AUTO_THROTTLE_SETTING);
143+
144+
// Index setting takes precedence over cluster setting
145+
if (MAX_FORCED_MERGE_MB_PER_SEC_SETTING.exists(indexSettings.getSettings())) {
146+
this.maxForcedMergeMBPerSec = indexSettings.getValue(MAX_FORCED_MERGE_MB_PER_SEC_SETTING);
147+
} else {
148+
this.maxForcedMergeMBPerSec = CLUSTER_MAX_FORCED_MERGE_MB_PER_SEC_SETTING.get(indexSettings.getNodeSettings());
149+
}
103150
}
104151

105152
/**
@@ -151,4 +198,20 @@ void setMaxThreadAndMergeCount(int maxThreadCount, int maxMergeCount) {
151198
public int getMaxMergeCount() {
152199
return maxMergeCount;
153200
}
201+
202+
/**
203+
* Returns the maximum forced merge rate in MB per second.
204+
* A value of Double.POSITIVE_INFINITY indicates no rate limiting.
205+
*/
206+
public double getMaxForcedMergeMBPerSec() {
207+
return maxForcedMergeMBPerSec;
208+
}
209+
210+
/**
211+
* Sets the maximum forced merge rate in MB per second.
212+
* A value of Double.POSITIVE_INFINITY disables rate limiting.
213+
*/
214+
void setMaxForcedMergeMBPerSec(double maxForcedMergeMBPerSec) {
215+
this.maxForcedMergeMBPerSec = maxForcedMergeMBPerSec;
216+
}
154217
}

server/src/main/java/org/opensearch/index/engine/OpenSearchConcurrentMergeScheduler.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,23 @@ void refreshConfig() {
224224
} else if (config.isAutoThrottle() == false && isEnabled) {
225225
disableAutoIOThrottle();
226226
}
227+
228+
// Apply forced merge rate limiting
229+
applyMergeRateLimit();
230+
}
231+
232+
/**
233+
* Applies the merge rate limit based on the current configuration.
234+
* If the setting value is Double.POSITIVE_INFINITY, rate limiting is disabled.
235+
* Otherwise, auto-throttling is enabled as the available rate limiting mechanism.
236+
*/
237+
private void applyMergeRateLimit() {
238+
double maxForcedMergeMBPerSec = config.getMaxForcedMergeMBPerSec();
239+
if (maxForcedMergeMBPerSec != super.getForceMergeMBPerSec()) {
240+
logger.info("[{}][{}] updating forced merge rate limit from [{}] to [{}] MB/sec",
241+
shardId.getIndexName(), shardId.id(), super.getForceMergeMBPerSec(), maxForcedMergeMBPerSec);
242+
super.setForceMergeMBPerSec(maxForcedMergeMBPerSec);
243+
}
227244
}
228245

229246
}

0 commit comments

Comments
 (0)