compute: two-runtime read isolation (interactive runtime)#37770
Draft
antiguru wants to merge 5 commits into
Draft
compute: two-runtime read isolation (interactive runtime)#37770antiguru wants to merge 5 commits into
antiguru wants to merge 5 commits into
Conversation
antiguru
force-pushed
the
mh/two-runtime-stage2
branch
from
July 21, 2026 20:16
2227313 to
d7c4425
Compare
…aring, interactive runtime) Squashed work-log for the two-runtime compute branch. Migrates production arrangements from Rc to Arc batches, adds cross-runtime arrangement sharing, and stands up a second in-process compute runtime that serves ephemeral peeks off the maintenance runtime's shared arrangements, fronted by a Multiplexer. Includes fixes landed while stabilizing the branch: - keep shared-trace snapshot import consistent so joins don't double-count - guard the shared-index snapshot bound against a Timestamp::MAX as_of - stop the multiplexer from dropping peek responses on non-zero processes - release transient read holds so two-runtime read frontiers advance: forward the interactive runtime's terminal empty-Frontiers report for a dropped transient even after ownership eviction, and report frontiers only for the transient collections the interactive runtime exclusively hosts Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CY7GHdrTBfAG4tgvSgPGJ9
antiguru
force-pushed
the
mh/two-runtime-stage2
branch
from
July 23, 2026 08:55
f90cdae to
6728fc8
Compare
antiguru
changed the base branch from
claude/spines-differential-arc-j93mho
to
main
July 23, 2026 09:02
Arc batches carry more fixed per-batch overhead than the Rc batches this check was written against, so a single-record index arrangement now exceeds the coarse `size < 16 * 1024` "not egregious" bound (the empty arrangement still fits). Bump the bound to 32 KiB, which one Arc batch on top of the empty spine stays under. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CY7GHdrTBfAG4tgvSgPGJ9
…w is dropped
A deferred interactive dataflow whose shared-index dependencies are not yet
published is cancelled from `pending_work` without ever building. The controller
had already created the collection, initialized its per-replica frontiers to the
as_of, and acquired read holds on its storage inputs. It releases those input
read holds only once the collection's frontiers all reach the empty antichain,
but the cancelled dataflow never reported any frontier, so the holds leaked and
pinned the inputs' read frontiers. This surfaced as an MV's `since` never
advancing under two-runtime (read_frontier_advancement).
Send empty `Frontiers` from `drop_collection` on the deferred-cancel path so the
controller can clean the collection up and drop the holds.
Also filter interactive-runtime frontier reports on `id.is_transient()` in the
multiplexer instead of `owner_of(id)` plus a terminal-empty special case. The
interactive runtime reports frontiers only for the transient collections it
hosts (see `report_frontiers`), so every such report must forward regardless of
`transient_owner`, whose eviction on `AllowCompaction{empty}` races ahead of a
dropped transient's trailing frontier reports. This replaces the earlier
terminal-transient hack and no longer depends on ownership state.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CY7GHdrTBfAG4tgvSgPGJ9
Measures peek p50/p99/qps under a saturated maintenance runtime. Two closed-loop `HydrationChurn` actions continuously build, hydrate, and drop heavy maintained MVs, keeping the maintenance workers busy. Two open-loop peeks (a fast-path index point lookup and a slower range-scan reduce) run at a fixed rate against a pre-hydrated indexed table. Run twice, once with `enable_two_runtime_compute=true` and once false, and compare the SELECT latencies. With two runtimes on, the interactive runtime serves the peeks off the shared arrangements, isolated from the maintenance workers, so the open-loop peeks accumulate far less queue-wait latency than when they contend with hydration on the same workers. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CY7GHdrTBfAG4tgvSgPGJ9
Two indexes on the same key make the optimizer build the second as a cross-dataflow re-export of the first: the second index's dataflow imports the first's arrangement and re-exports it under its own id. On the maintenance runtime, render's `ArrangementFlavor::Trace` arm publishes that re-export into the sharing registry via `reexport`, but unlike the `Local` arm it builds no streams of its own and so installs no seal-signal frontier tap. An interactive peek on the re-exported id that arrives when the arrangement's `upper` equals the peek `as_of` defers in `pending_work` waiting for the seal. Nothing ever fires `note_frontier` for the re-exported id when it advances, so the peek is never re-examined and hangs. A `DROP INDEX` of one of two same-key indexes followed by a `SELECT` hit this deterministically. Have the registry track re-export aliases. `reexport(from, to)` records `to` as an alias of `from`, and `notify` marks an id together with the transitive closure of ids that re-export it. The source's own dataflow outlives its catalog drop while a re-export still imports its arrangement, so its live tap keeps sealing the re-export. `remove` prunes an id only as a target, never as a source, so that trailing seal signal survives the source's `DROP INDEX`. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CY7GHdrTBfAG4tgvSgPGJ9
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.
A second in-process "interactive" compute runtime that serves reads against the arrangements the maintenance runtime maintains, isolating latency-sensitive reads from CPU-bound maintenance. Implements the two-runtime design (#37747). Stacks on the Arc-batches branch (#37743) and consumes the differential shared-trace primitive (TimelyDataflow/differential-dataflow#807), so the base is that branch, not
main.How it works:
oks/errsarrangements into a per-processArrangementSharingRegistry. AComputeRuntimeRole { Solo, Maintenance, Interactive }distinguishes runtimes;Solo(default, single-runtime) is byte-behaviorally unchanged, metrics included.PeekResponseper uuid, and each collection's frontiers only from the runtime that owns it (both runtimes install the internal logging dataflows, so this keeps the interactive runtime's empty copies from regressing the controller's per-collection frontier).ArrangementFlavor::SharedTracearrangements (not re-derived collections) and render joins/reduces over them; the query's own transient output is itself published so its result peek is served the same way. A spike verified a join over an imported shared arrangement is correct (the join cuts each trace at its own acknowledged batch boundary, so the shared handle'sbatches_throughis always batch-aligned).Maintenancerole, since those indexes bypass the normalexport_indexpublish path). The interactive runtime runs with logging disabled and serves introspection peeks from maintenance's published copies, so introspection queries during hydration return promptly (possibly stale) instead of blocking behind maintenance.SyncActivator+ dirty-id inbox. A read whose dependency is not yet published/sealed is enqueued, not blocked; publication (insert) and seal (note_frontier, fired from the maintenance export's frontier probe) mark the id dirty and wake the interactive worker, which re-examines only the affected pending work. A query dataflow whose imported deps aren't published yet is deferred (not built) and built once all deps publish. The per-stepprocess_peeksscan is removed on the interactive runtime.ENABLE_TWO_RUNTIME_COMPUTE(dyncfg, off by default in production) makes the controller launch replicas with a second interactive runtime (--interactive-compute-timely-config, its own worker ports). Enabled by default in the variable CI system parameters so the suite exercises the two-runtime path broadly.Acceptance: a
clusterd-test-driverworkflow drives an interactive query dataflow that imports an unpublished maintenance index, scheduled before the index publishes (mirroring controller ordering), and its result peek resolves correctly only after publication — proving the defer→build→resolve read path is served off the maintenance worker.Design and per-task plans: #37747,
doc/developer/design/20260720_two_runtime_compute/stage2-detailed-plan.md.Scope / follow-ups:
until/as_ofcoalescing (correct for one-shot peeks; a future subscribe migration must add it).index_peek_total_seconds; pre-existingprintln!debug lines inrender.rsNonearms.🤖 Generated with Claude Code
https://claude.ai/code/session_01DAfFVVJ8BwahdaAHkRtbf6