Skip to content

design: two-runtime compute read isolation - #38239

Open
antiguru wants to merge 6 commits into
MaterializeInc:mainfrom
antiguru:mh/two-runtime-design
Open

design: two-runtime compute read isolation#38239
antiguru wants to merge 6 commits into
MaterializeInc:mainfrom
antiguru:mh/two-runtime-design

Conversation

@antiguru

@antiguru antiguru commented Aug 17, 2026

Copy link
Copy Markdown
Member

Motivation

A compute replica maintains indexes and serves reads on the same timely workers, and timely does not preempt a running task. So a read waits behind maintenance work, not because the machine is out of CPU but because the one run loop is busy and cannot be interrupted. The sharpest form is introspection: mz_introspection describes a replica's own state and cannot be served from any other replica, so it is unavailable exactly when an operator needs it.

This is design only. No production code. It is the design of record for the implementation stack, #38386 through #38393, with the benchmarks in #38676 and #38678, and it describes what those pull requests ship.

Description

One document, design.md. It targets the interactive runtime: a second in-process timely runtime that renders temporary dataflows and serves reads off the maintenance runtime's arrangements, zero-copy through a per-process sharing registry.

It opens with the decomposition that shaped everything else: twelve reported symptoms, the mechanisms behind them, and which of sixteen candidate solutions reaches each. Every cell carries an evidence label distinguishing measured from argued from read-off-the-code, and the table deliberately includes mechanisms nothing here addresses. Two of the widest-blast-radius ones are among those, which is the point: a decomposition listing only the causes we have answers for is not a decomposition.

Several claims the work started with are retired in it:

  • The peek walk substrate, not the second runtime, is what removes head-of-line blocking between peeks.
  • Moving the walk off the worker is reproducibly worse than doing nothing when a peek queues behind a long operator activation.
  • The second runtime's case is temporary dataflows and introspection during hydration. Judging the sharing registry and its protocol against a peek-latency claim weighs them against the wrong benefit.

Peek placement is therefore treated as an orthogonal axis and discussed once. It needs no second runtime and this design needs none of it. It shipped separately, as budgeted peek slices on the worker with offload past the budget, and #38449 is its design. The interactive runtime inherits that path unchanged.

The protocol section describes what shipped: broadcast compaction and a standing hold per shared collection, including the counterexample that rules out broadcasting without the hold and the liveness coupling the hold introduces. Capping the frontier at the multiplexer, a synthesized AcquireHolds command, and an in-process sequence barrier are recorded as rejected alternatives with the reason each failed.

What the current revision changed

The document now matches the stacked implementation rather than the branch it was split from.

  • Re-exports are aliases. An index that re-exports another's arrangement registers its id as a second name for the target's publication point, so the re-export dataflow has no operators and costs no resident memory. The document records the import-based fallback for a reader that bound first, how the target's frontiers govern the shared point, and the meet over aliases once the target drops.
  • Two findings from the adversarial review were fixed and are documented as mechanism rather than kept as defects: the reader's handle mirrors TraceAgent (join on set, physical hold seeded at the chain coverage), and the import keeps no hold pinned at as_of, so a long-lived importer's compaction feeds back to the publisher.
  • Fast-path peeks on the interactive runtime go through the unified IndexPeek path, with the trace provenance as the only difference.
  • The testing strategy lists the driver specs, the sqllogictest, the feature and parallel benchmark scenarios and the swap scenarios, with the numbers one nightly run produced. The introspection read under hydration churn is the headline: p50 94 ms against 595 s with one runtime.

What moved out

The experimental evaluation is a project document under Interactive read isolation, which also tracks the work as issues. Evaluation artifacts do not belong in a design.

Both model checks are removed with their CI jobs. The TLA+ spec modelled the shipped mechanism; the Lean model's scope was the controller's read hold and the multiplexer's cap, and both of its counterexamples target behaviours no longer present in any form. The reasoning each contributed survives as prose in the rejected alternatives.

🤖 Generated with Claude Code

https://claude.ai/code/session_01VDm7opomJLxbNUEP3r9BLk

@antiguru
antiguru requested a review from a team as a code owner August 17, 2026 08:09
antiguru added a commit to antiguru/materialize that referenced this pull request Aug 17, 2026
The design of record, the evaluation record and the two machine-checked protocol
models land separately in MaterializeInc#38239, along with their CI checks. They have no
dependency on this code and reviewing 4,700 lines of prose against 7,000 lines of
compute is worse for both.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019G29DBfgE8LXpE5jamm2Zi
The design of record for isolating reads from index maintenance inside a compute
replica, plus the two machine-checked protocol models and the experiment record
behind it.

`design.md` opens with the decomposition that shaped everything else: twelve
reported symptoms, the mechanisms behind them, and which of sixteen candidate
solutions reaches each. Every cell carries an evidence label, and the table
deliberately includes mechanisms nothing here addresses, because a decomposition
listing only the causes we have answers for is not a decomposition. Two of the
widest-blast-radius mechanisms are among the unaddressed.

`evaluation.md` records the experiments with their fixtures and venues. Several
retired claims the design started with. The offload alone captures the peek-tail
win, so the second runtime is not what removes head-of-line blocking between
peeks. The offload is measurably worse than doing nothing when a peek queues
behind a long operator activation. And the second runtime's case is temporary
dataflows and introspection during hydration, not peeks at all.

`peek-placement.md` parks the peek-substrate question as orthogonal, with the
deciding experiment and its rule registered in advance rather than settled by
implementing every candidate and learning afterwards.

The models are checked in CI, because a model nobody runs reads as assurance
without being any. `protocol-holds/` is TLA+ over the read-hold protocol and runs
three configs: the shipped design, plus two that are expected to violate the
invariant and whose passing would mean the model can no longer express the defect
the design fixes. It earned its place by refuting broadcast-alone before that was
implemented. `protocol/` is a Lean development of the command-ordering argument.
`check-copyright.sh` gains an exemption for `lean-toolchain`, which is a version
pin with nowhere to put a header.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019G29DBfgE8LXpE5jamm2Zi
antiguru added a commit to antiguru/materialize that referenced this pull request Aug 21, 2026
The design of record, the evaluation record and the two machine-checked protocol
models land separately in MaterializeInc#38239, along with their CI checks. They have no
dependency on this code and reviewing 4,700 lines of prose against 7,000 lines of
compute is worse for both.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019G29DBfgE8LXpE5jamm2Zi
@antiguru
antiguru force-pushed the mh/two-runtime-design branch from c6e43ec to bd6bfb6 Compare August 21, 2026 07:39
The directory held seven documents, three of which covered the read-hold
mechanism and two of which were spent scaffolding. It now holds one design
document that targets the interactive runtime.

The protocol section described the wrong mechanism. It specified capping the
compaction frontier at the multiplexer, which the implementation replaced with
broadcast compaction and a standing hold per shared collection. That section is
rewritten from `broadcast-compaction.md`, including the counterexample that
rules out broadcasting without the hold and the liveness coupling the hold
introduces. The cap, the synthesized `AcquireHolds` command, and the in-process
sequence barrier are recorded as rejected alternatives, with the reason each
failed: the cap failed at its retirement point rather than at the cap, since
`send` carries no ack.

Both model checks are removed, along with their CI jobs and images. The TLA+
spec modelled the shipped mechanism and the Lean model did not: its scope was
the controller's read hold and the multiplexer's cap, and both of its
counterexamples target behaviours no longer present in any form. The reasoning
each contributed survives as prose in the rejected alternatives.

Peek placement becomes an orthogonal axis discussed once rather than an
argument threaded through the document. The solution matrix keeps its row,
because the comparison is what bounds the interactive runtime's claims, but the
adjudication between placement candidates, the substrate follow-ups and the
routing policy move to the tracking issues. `peek-placement.md` and
`peek-offload-stash-plan.md` move to the branch that implements the offload.

The experimental evaluation moves to a project document. Evaluation artifacts
do not belong in a design, and the document also carried experiment protocol
for arms the code no longer reaches the same way.

Also corrected: the serving path claimed walking off the worker was abandoned,
the arrangement-size doubling is recorded as not reproducing on staging, and
the open findings drop the items describing deleted code.
antiguru and others added 4 commits September 6, 2026 07:06
The document described the branch as it stood before the implementation was
split into reviewable layers, and three parts of it no longer matched the code.

Peek execution shipped separately. The summary and the placement section said
peek offloading was parked pending an experiment; it now records that the walk
runs in budgeted slices with offload past the budget, that both are dyncfgs, and
that the interactive runtime inherits that path. The serving path section
replaces the runtime's own pending-peek variant with the unified `IndexPeek`
over `IndexTraces`, and the registry section describes how a shared peek parks
in `pending_work` until its target's dirty mark.

Two open findings were fixed and are documented as mechanism rather than kept as
defects. The reader's handle mirrors `TraceAgent`: it joins on set, reports the
join, and seeds its physical hold at the chain coverage, so the inert physical
hold finding is removed and the compaction section explains why the coverage is
the only correct seed. The import keeps no separate hold pinned at `as_of`; the
read hold is the returned arrangement's own trace handle, so the frozen-hold
section becomes a description of the feedback path.

Re-exports are aliases. The registry section replaces the sentence about sharing
an `Arc` under a new id with the alias mechanism: `publish_alias`, its refusal
when a reader bound first and the import-based fallback that covers it, the
target-governed frontiers, and the meet over aliases after the target drops. The
discarded-failure finding is removed and the measured cost of the earlier
import-based publication is recorded.

The multiplexer, configuration and testing sections are updated to the shipped
routing predicate, the soft assertion on transient imports, the CI variable
system parameter, the four driver specs, the sqllogictest, the feature and
parallel benchmark scenarios, and the swap scenarios, with the numbers one
nightly run produced. The implementation history lists the stacked pull
requests.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VDm7opomJLxbNUEP3r9BLk
The shared import follows the dataflow's `until` instead of synthesising a bound
one step past `as_of`, so the section that presented the bound as a call-site
narrowing now places the single-time restriction where it lives, in the routing
predicate. An export whose arrangement is an imported shared arrangement aliases
its publication point instead of hitting an `unreachable!`, which closes that
open finding and adds a fifth driver spec. The interactive runtime installs no
logging dataflow, so the runtime tells the peer's publications apart by whether
it hosts the collection rather than by transience, and the multiplexer's
frontier filter is described as a guard on that invariant rather than as a
repair for empty copies. The registry's state sits behind one lock.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VDm7opomJLxbNUEP3r9BLk
The sharing primitive became `SharedSpine`, a `Trace` wrapper that mirrors the
spine's chain and frontiers into the publication point inside each mutation. The
design records why a spine behind a mutex was rejected, with the measured reader
and writer latencies, replaces the sink-based feed and compaction sections, drops
the forwarded controller frontier, and marks the arrangement-size doubling as gone
with the sink.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VDm7opomJLxbNUEP3r9BLk
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