compute: run MV sink correction maintenance on a blocking thread - #38118
Draft
antiguru wants to merge 1 commit into
Draft
compute: run MV sink correction maintenance on a blocking thread#38118antiguru wants to merge 1 commit into
antiguru wants to merge 1 commit into
Conversation
The sync MV sink's `write` Tokio task performed correction-buffer maintenance inline. Inserts merge chains spanning the whole buffer and consolidation sorts it, neither with an await point in between, so a pass over a large buffer occupied a Tokio worker thread for its entire duration and stopped it polling everything else scheduled there. Persist lease heartbeats were among the casualties: readers lost their leases and the process halted with "batch fetcher could not fetch batch part". Under memory pressure the pass is not merely slow. The buffer's chunks page back in one blocking read at a time, from inside the sorts and merges, so the thread is blocked in the kernel rather than computing. That rules out fixing this by yielding, since there is no program point between the faults at which a yield could be placed. Running the work on a blocking thread instead lets the OS preempt it, which holds regardless of what the buffer does. `Correction::updates_before` is split into `consolidate_before`, which does the expensive part, and `consolidated_updates_before`, which reads the result back. `updates_before` is now the composition of the two, so the async sink and the benchmarks are unaffected. The write task runs `consolidate_before` on a blocking thread and then feeds the updates to the persist batch builder from the async task, where the loop awaits at every part flush and so stays bounded. Adds `write::tests::correction_work_does_not_stall_the_runtime`, which stalls every worker thread of a two-worker runtime inside the correction path via a new `mv_sink_correction` failpoint and asserts an unrelated task keeps getting polled. The failpoint also drives the end-to-end lease-expiry scenario when set through the `FAILPOINTS` environment variable on a clusterd process. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Follow-up to #38074, which disabled
ENABLE_SYNC_MV_SINKby default after incident 1175.The sync MV sink's
writeTokio task performed correction-buffer maintenance inline. Inserts merge chains spanning the whole buffer and consolidation sorts it, neither with an await point in between, so a pass over a large buffer occupied a Tokio worker thread for its entire duration and stopped it polling everything else scheduled there. Persist lease heartbeats were among the casualties: readers lost their leases and the process halted withbatch fetcher could not fetch batch part. The async sink never hit this, because its operators run on the Timely thread.Under memory pressure the pass is not merely slow.
CorrectionV2pages its chunks out through the column pager, and consolidation callsChunk::columnon every chunk it merges, so the chunks come back one blocking read at a time from inside the sorts and merges. The thread is blocked in the kernel rather than computing. That rules out fixing this by yielding: there is no program point between the faults at which a yield could be placed, and a cooperative fix is only ever as strong as the worst un-yielded path. Running the work on a blocking thread lets the OS preempt it, which holds regardless of what the buffer does.Correction::updates_beforeis split intoconsolidate_before, which does the expensive part, andconsolidated_updates_before, which reads the result back.updates_beforebecomes the composition of the two, so the async sink and the benchmarks are unaffected. TheCorrectionV2sinceguard moves fromupdates_beforeintoconsolidate_before;consolidate_at_sincealways passes anupperbeyond thesince, so it is unaffected.The write task runs the correction work on a blocking thread and then feeds the updates to the persist batch builder from the async task, where the loop awaits at every part flush and so stays bounded.
The cost is a thread hop per command, one or two per Timely activation. Note that
persist-client'sIsolatedRuntimedocuments a preference againstspawn_blockingfor CPU-bound work, on thread-count and shutdown grounds. It is used here deliberately: the failure mode is a thread blocked in the kernel, and OS preemption is what addresses that.This does not re-flip the
ENABLE_SYNC_MV_SINKdefault, which staysfalsepending gradual rollout via LaunchDarkly. mzcompose continues to set the parameter totrue, so CI exercises the path.Adds
write::tests::correction_work_does_not_stall_the_runtime, which stalls every worker thread of a two-worker runtime inside the correction path via a newmv_sink_correctionfailpoint and asserts an unrelated task keeps getting polled. Moving theapply_batchbody back out ofspawn_blockingfails it. The failpoint also drives the end-to-end lease-expiry scenario when set through theFAILPOINTSenvironment variable on a clusterd process; thefailpointssession variable only reaches environmentd, never a cluster.Release notes
This release will not include user-visible changes.
🤖 Generated with Claude Code