Skip to content

adapter: keep timestamp oracle round trips off the coordinator loop for object memory arrangements - #38915

Open
leedqin wants to merge 1 commit into
MaterializeInc:mainfrom
leedqin:arrangement-sizes-oracle-offload
Open

leedqin wants to merge 1 commit into
MaterializeInc:mainfrom
leedqin:arrangement-sizes-oracle-offload

Conversation

@leedqin

@leedqin leedqin commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Motivation

#37455 moved arrangement size history snapshotting's persist reads off the coordinator loop, but three timestamp oracle round trips stayed on it. The oracle is backed by the metadata store, so each call is a network round trip with unbounded tail latency. After the feature was re-enabled in staging, arrangement_sizes_snapshot showed ~976ms p95 on the Slow Coordinator Messages panel, against a target of <10ms for anything on the coordinator task.

The remaining round trips:

  • schedule_arrangement_sizes_collection awaited peek_local_write_ts on entry. Sleeps are capped at MAX_SLEEP, so this re-entered every 60 seconds even while collection was disabled, and again on every skip path.
  • arrangement_sizes_write awaited get_local_write_ts once per cycle to allocate a write timestamp used only as a display stamp, never applied to a write.
  • All four handlers were dispatched as futures via .boxed_local().await.

Changes

  1. The oracle peek moves into the task that already sleeps and snapshots. Only the dyncfg read and the organization_id-seeded offset derivation stay on the loop.
  2. collection_ts is stamped from the snapshot's own read_ts and carried on Message::ArrangementSizesWrite, rather than allocating a second timestamp in the write handler. read_ts is the state the rows describe, is monotone across restarts via the oracle, and matches the startup prune cutoff, which is already read_ts based.
  3. With no remaining await points, all four handlers become sync fns and the dispatch arms call them directly.

The result is structural rather than incremental: a sync fn cannot await, so this path can no longer block the loop regardless of future edits, and the constraint is enforced by the compiler rather than by review.

Tests

No tests added or modified. This is a threading change with no intended behavior change, and it is covered by the existing arrangement_sizes unit tests, test/testdrive/arrangement-sizes.td, and test/restart workflow_arrangement_sizes_stale_snapshot_after_restart.

One behavioral nuance for reviewers: because the snapshot now runs entirely off-thread, a replica can go offline while it is in flight, so arrangement_sizes_write revalidates introspection freshness before appending and drops records from replicas that are no longer trusted.

Follow-up

collection_ts is no longer a uniquely allocated timestamp, so two snapshots landing in the same millisecond would share one. Not reachable at the hourly production cadence.

Verification that this actually clears the spike is post-deploy: re-enable arrangement_size_history_collection_interval in staging and confirm the arrangement_sizes_* series stay flat, with oracle tail latency appearing only in arrangement_sizes_collection_time_seconds.

🤖 Generated with Claude Code

Arrangement size history snapshotting moved its persist reads off the
coordinator loop, but three timestamp oracle round trips stayed on it.
The oracle is backed by the metadata store, so each call is a network
round trip with unbounded tail latency, and staging showed ~976ms p95 on
`arrangement_sizes_snapshot` once the feature was re-enabled.

The remaining round trips were:

  * `schedule_arrangement_sizes_collection` awaited `peek_local_write_ts`
    on entry. Sleeps are capped at `MAX_SLEEP`, so this re-entered every
    60 seconds even while collection was disabled, and again on every
    skip path.
  * `arrangement_sizes_write` awaited `get_local_write_ts` once per
    cycle to allocate a write timestamp used only as a display stamp,
    never applied to a write.
  * All four handlers were dispatched as futures via
    `.boxed_local().await`.

Move the oracle peek into the task that already sleeps and snapshots, so
only the dyncfg read and the offset derivation run on the loop. Stamp
`collection_ts` from the snapshot's own `read_ts`, which is the state the
rows describe and is monotone across restarts via the oracle, and carry
it on `Message::ArrangementSizesWrite` instead of allocating a second
timestamp. This also drops an allocation that was never applied, and
matches the startup prune cutoff, which is already `read_ts` based.

With those gone the snapshot and write handlers have no await points and
prune never had any, so all four become sync fns and the dispatch arms
call them directly. The loop can no longer block on this path: a sync fn
cannot await, so the constraint is enforced by the compiler rather than
by review.

One nuance: `collection_ts` is no longer a uniquely allocated timestamp,
so two snapshots landing in the same millisecond would share one. That
is not reachable at the hourly production cadence.
@leedqin leedqin changed the title adapter: keep timestamp oracle round trips off the coordinator loop adapter: keep timestamp oracle round trips off the coordinator loop for object memory arrangements Sep 17, 2026
@leedqin
leedqin marked this pull request as ready for review September 17, 2026 15:48
@leedqin
leedqin requested a review from a team as a code owner September 17, 2026 15:48
@leedqin
leedqin requested a review from ggevay September 17, 2026 15:48
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