-
Type:
Task
-
Resolution: Unresolved
-
Priority:
Major - P3
-
None
-
Affects Version/s: None
-
Component/s: None
-
None
-
Storage Execution
-
Storage Execution 2026-08-17
-
None
-
None
-
None
-
None
-
None
-
None
-
None
Add a server parameter to throttle the rate at which the collection scan phase of a primary-driven (background) index build writes – that is, the rate at which it spills sorted keys into the sorter's container – in order to reduce the index build's impact on foreground workload throughput on disaggregated storage clusters.
To be explicit about scope: the target is the write rate, not the document read rate. The scan phase's cost to the foreground workload on DSC comes from the replicated container writes it generates, so that is what must be limited.
Goal 1 of the scope document requires that an index build be no more impactful to foreground operation throughput on DSC than the same index build on ASC. The scan phase is the first of two phases whose writes need a throttle; the bulk load phase is handled separately.
Implementation notes
- Reuse DataThrottle (src/mongo/db/throttle_cursor.h:132) rather than writing a bespoke sleep loop. awaitIfNeeded(opCtx, dataSize) enforces an MB/sec byte budget, is interruptible via the OperationContext, and takes a std::function<int()> supplier so the parameter is live-settable. A value of 0 MB/sec means disabled, which gives us off-by-default behavior for free. Existing consumers, for precedent: validate (validate/validate_state.h) and dbcheck.
- Throttle point: ContainerBasedSpiller::_spill (src/mongo/db/sorter/container_based_spiller.h:648-684). That method already writes the buffer in batches of _batchSize entries / _batchBytes bytes, each in its own writeConflictRetry + WriteUnitOfWork (:654-678), so the throttle goes at that existing batch boundary, after the unit of work commits, charging the batch's byte count. Batch sizes come from primaryDrivenIndexBuildSorterInsertionBatchSize / ...BatchBytes (multi_index_block.cpp:201-202, :214-215).
- Also throttle ContainerBasedSpiller::_mergeSpills (container_based_spiller.h:542-642), which writes a merged range batch-wise (:615) and then deletes the source key range batch-wise (:617-632). Merge spills are triggered opportunistically at the end of every spill (sorter/sorter_template_defs.h:752-755), so they are part of the scan phase's write load and would otherwise escape the limit entirely.
- Sleeping inside the spill is likely safer than sleeping in the scan loop: the preSpill callback (multi_index_block.cpp:550-577) already saves plan-executor state before the spill runs, and postSpill restores it afterward, so the storage snapshot should not be held across the sleep. This should be confirmed during implementation.
- The throttle must apply only to the replicated/container path (ContainerWriteBehavior::kReplicate); the file-based spiller used off-disagg is out of scope. Foreground index builds must be unaffected.
- The new parameter belongs in src/mongo/db/index_builds/primary_driven_index_build_knobs.idl (namespace mongo::index_builds::primary_driven, mod_visibility: private), following the pattern of primaryDrivenIndexBuildIndexTableCleanupBatchBytes.
This supersedes the POC in SERVER-128750, which never merged to any branch, and which throttled the scan loop by documents read rather than by bytes written.
- is related to
-
SERVER-128750 POC rate-limit primary-driven index build collection scan
-
- Closed
-