Arc-backed spines for cross-thread arrangement sharing#807
Draft
antiguru wants to merge 7 commits into
Draft
Conversation
Add `arc_blanket_impls`, a mirror of `rc_blanket_impls` using `Arc`, and `ArcOrdValSpine`/`ArcOrdKeySpine` with their `ArcOrdValBuilder`/`ArcOrdKeyBuilder` alongside the existing `Rc` spines and builders. Also re-export them as `ArcValSpine`/`ArcKeySpine`/`ArcValBuilder`/`ArcKeyBuilder`. The addition is purely additive. The default `OrdValSpine`/`OrdKeySpine` and `Val`/`Key` aliases remain `Rc`-backed, so existing users are unaffected. An `Arc`'d batch whose contents are `Send + Sync` can be handed to and read from a thread other than the one maintaining the trace, which the `Rc` variants cannot do. Atomic reference counting is marginally more expensive, so `Rc` stays the default and callers opt into `Arc` only when they need the sharing. A test reads an `Arc`'d batch from another thread. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FwFQJa3HSy8oGJZ3X9uGZx
Add operators::arrange::sharing, a primitive for reading an arrangement from outside the worker that maintains it, built on the Arc-backed spines. Arranged::publish attaches a publisher operator to an arrangement on the owning worker and returns a Published, whose handle() mints Clone + Send SharedTraceHandles. A handle implements TraceReader, so downstream operators drive its compaction and acquire cursors as with any trace handle. SharedTraceHandle::snapshot_at serves a consistent point or full-scan read from any thread, blocking until the publication frontier passes the query time. SharedTraceHandle::import replays the shared arrangement into another scope, so a second timely runtime can consume it as an ordinary Arranged. The publisher forwards the meet of reader holds to its own TraceAgent, the sole writer of the trace's compaction, never the empty meet of zero holds (which would irreversibly release the trace). Handle clones carry independent holds, so two consumers of one import cannot release each other's holds. Compaction is applied to the agent outside the shared lock, since physical compaction can run an unbounded merge. The published since is the frontier the trace will actually hold after forwarding, so a handle registering concurrently cannot latch a floor below the trace's real compaction. A late importer registering after the publisher closed seeds its own terminal frontier, so it drains and releases rather than hanging. Tests run a cross-thread snapshot and a cross-dataflow import through a shared handle. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FwFQJa3HSy8oGJZ3X9uGZx
…peers work Corrections from adversarial review and the two-runtime compute integration design: - snapshot_at returns None when since has advanced beyond the requested time, so a point read past compaction fails safe instead of returning coalesced results. Mirrors the single-runtime peek path's since gate. - batches_through asserts a non-empty batch does not straddle the requested cut, matching Spine::batches_through, so an import cannot silently return updates at times not before the cut and corrupt a downstream cursor_through. - TODO documenting that publishing currently pins compaction: the publisher forwards its own frozen hold, which never advances and pins the trace's effective since at publish time. The fix is design-laden (publishing must carry no independent floor) and is left to the integration with tests. - TODO documenting the equal-peers assertion import should carry. Read holds registered by import are correct and intended. Existing sharing and trace tests pass. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FwFQJa3HSy8oGJZ3X9uGZx
antiguru
force-pushed
the
claude/spines-differential-arc-j93mho
branch
from
July 20, 2026 19:25
08ecc0d to
f497181
Compare
Pairwise import (importer worker i reads publisher worker i) is sound only when both runtimes shard keys by the same total peer count. Record the publisher's peers at publish time and assert scope.peers() matches it in import, so a mismatch fails loudly instead of silently reading the wrong shard. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Publishing an arrangement through the shared-trace primitive froze the trace's logical compaction at the publish-time since for the life of the arrangement, even with zero readers, because the publisher's own agent hold pinned the trace-box meet and never advanced. Source the writer-driven frontier from the trace box's accumulated holds with the publisher's own contribution removed, and advance the publisher's hold to it so it follows the writer instead of pinning. The per-batch since cannot serve as this source: it only advances when the Spine compacts, which the publisher's own hold prevents. Publish exactly the trace's real compaction so a live importer hold keeps its own frontier readable. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Imports a published arrangement as a static snapshot at `as_of`, emitting the sealed chain once (when `upper` reaches `as_of`) through a TraceFrontier/ BatchFrontier wrapper advanced to `as_of` and bounded by `until`, then releasing the capability. Unlike the live `import`, there is no forward change stream, so the forward-batch capability handling and the lack of `as_of` coalescing (both unsound for a read at `as_of`) do not apply. Times are advanced on read, so the shared Arc batches are reused, never re-arranged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
antiguru
force-pushed
the
claude/spines-differential-arc-j93mho
branch
from
July 21, 2026 20:14
b6afe61 to
b57e366
Compare
Add public TraceBox::logical_compaction()/physical_compaction() accessors returning the accumulated MutableAntichain holds of all referees. This lets an external referee (a cross-runtime arrangement publisher living in a downstream crate) compute the frontier the trace would compact to without its own hold, which previously required reading the pub(crate) fields from inside this crate. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
antiguru
marked this pull request as draft
July 22, 2026 11:44
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.
Summary
Adds the ability to read an arrangement from a thread other than the one that maintains it, in additive commits.
trace: add Arc-backed spine variants for cross-thread sharingAdds
arc_blanket_impls(a mirror ofrc_blanket_implsusingArc) and theArcOrdValSpine/ArcOrdKeySpinespines with theirArcOrdValBuilder/ArcOrdKeyBuilder, re-exported asArcValSpine/ArcKeySpine/ArcValBuilder/ArcKeyBuilder.This is purely additive. The default
OrdValSpine/OrdKeySpine, theRcOrd*Builders, and theVal*/Key*aliases stayRc-backed, so existing users are unaffected. AnArc'd batch whose contents areSend + Synccan be handed to and read from another thread, which theRcvariants cannot. Atomic reference counting is marginally more expensive (per batch clone/drop, never per record), soRcremains the default and callers opt intoArconly when they need the sharing.arrange: share arrangements across timely runtimesAdds
operators::arrange::sharing, a primitive for reading an arrangement from outside the owning worker, built on theArc-backed spines:Arranged::publishattaches a publisher operator to an arrangement and returns aPublished, whosehandle()mintsClone + SendSharedTraceHandles.SharedTraceHandleimplementsTraceReader, so downstream operators drive its compaction and acquire cursors as with any trace handle.SharedTraceHandle::snapshot_atserves a consistent point/full-scan read from any thread, blocking until the publication frontier passes the query time and gating onsince <= tso a read past compaction fails rather than returning stale rows.SharedTraceHandle::importreplays the shared arrangement into another scope, so a second timely runtime can consume it as an ordinaryArranged.The publisher forwards the meet of reader holds to its own
TraceAgent(the sole writer of the trace's compaction), never the empty meet of zero holds. Handle clones carry independent holds. Compaction is applied to the agent outside the shared lock, since physical compaction can run an unbounded merge. The publishedsinceis the frontier the trace will actually hold after forwarding, so a concurrently registering handle cannot latch a floor below the trace's real compaction. A late importer registering after the publisher closed seeds its own terminal frontier rather than hanging.arrange/sharing: import asserts equal total peersRecords the publisher's total peer count at publish time and asserts an importing scope's
peers()matches it. Pairwise import (importer workerireads publisher workeri) is sound only when both sides shard bykey.hashed() % peers, which requires equal total peers (workers-per-process times processes), not just equal workers-per-process. A mismatch now panics at import time instead of silently reading the wrong shard.arrange/sharing: publishing no longer pins compactionPreviously the publisher held its own
TraceAgentat the publish-timesince, which never advanced, so merely publishing an arrangement froze the trace's compaction for the life of the arrangement even with zero readers. The publisher now sources the writer-driven compaction frontier from the trace box (the meet of all other handles' holds, i.e. the box's counted meet minus the publisher's own contribution) and forwards that when no reader holds exist, and the meet of reader holds when they do. Publishing carries no independent compaction floor: only a live importer's ownas_ofhold holds the trace back, released on drop. The publishedsinceequals the trace's real compaction, so a live reader can still read at times it holds.Motivation
This unblocks sharing an arrangement between two adjacent timely runtimes in one process (for example, a maintenance runtime and a latency-sensitive query runtime), and reading arrangement snapshots from non-timely threads.
Testing
tests/trace.rs: reads anArc'd batch from another thread.tests/sharing.rs: a cross-thread snapshot, a cross-dataflow import through a shared handle,publish_without_readers_does_not_pin_compaction,import_hold_pins_then_releases, andimport_asserts_equal_peers.Rcdefault path is unchanged.🤖 Generated with Claude Code