Skip to content

compute: two-runtime read isolation (interactive runtime)#37770

Draft
antiguru wants to merge 3 commits into
MaterializeInc:mainfrom
antiguru:mh/two-runtime-stage2
Draft

compute: two-runtime read isolation (interactive runtime)#37770
antiguru wants to merge 3 commits into
MaterializeInc:mainfrom
antiguru:mh/two-runtime-stage2

Conversation

@antiguru

@antiguru antiguru commented Jul 21, 2026

Copy link
Copy Markdown
Member

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:

  • Maintained indexes publish their oks/errs arrangements into a per-process ArrangementSharingRegistry. A ComputeRuntimeRole { Solo, Maintenance, Interactive } distinguishes runtimes; Solo (default, single-runtime) is byte-behaviorally unchanged, metrics included.
  • A process-level multiplexer presents one controller endpoint, routing peeks and one-shot work to interactive, maintained work to maintenance, exactly one PeekResponse per 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).
  • The interactive runtime serves everything through the registry: fast-path index peeks read the published arrangement directly; slow-path query dataflows (e.g. introspection views) import the maintenance arrangements as real ArrangementFlavor::SharedTrace arrangements (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's batches_through is always batch-aligned).
  • Introspection reads come from maintenance. Maintenance publishes its logging/introspection indexes into the registry too (gated strictly on the Maintenance role, since those indexes bypass the normal export_index publish 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.
  • Notification-driven, no polling. The registry carries a per-interactive-worker coalescing 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-step process_peeks scan is removed on the interactive runtime.
  • Controller plumbing: 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.
  • Failure model: shared-fate abort covers both runtimes (subprocess test); dropping a still-deferred interactive dataflow cancels cleanly rather than panicking.

Acceptance: a clusterd-test-driver workflow 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:

  • Subscribe is out of scope: all interactive work is single-time, so the shared import applies no until/as_of coalescing (correct for one-shot peeks; a future subscribe migration must add it).
  • A full SQL-level "introspection during hydration" acceptance test (versus the protocol-level clusterd-test-driver one here) is a follow-up; the mechanism it would exercise is in place.
  • Minor: the inline interactive peek records no index_peek_total_seconds; pre-existing println! debug lines in render.rs None arms.

🤖 Generated with Claude Code

https://claude.ai/code/session_01DAfFVVJ8BwahdaAHkRtbf6

@antiguru antiguru changed the title compute: two-runtime read isolation (Stage 1 offload + Stage 2 second runtime) compute: two-runtime read isolation (interactive runtime) Jul 21, 2026
@antiguru
antiguru force-pushed the mh/two-runtime-stage2 branch from 2227313 to d7c4425 Compare July 21, 2026 20:16
…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
antiguru force-pushed the mh/two-runtime-stage2 branch from f90cdae to 6728fc8 Compare July 23, 2026 08:55
@antiguru
antiguru changed the base branch from claude/spines-differential-arc-j93mho to main July 23, 2026 09:02
antiguru and others added 2 commits July 23, 2026 11:38
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant