Skip to content

feat(master): pure demand fold over per-shard watermarks - #344

Merged
beinan merged 1 commit into
lance-format:mainfrom
beinan:feat/demand-fold
Oct 8, 2026
Merged

beinan merged 1 commit into
lance-format:mainfrom
beinan:feat/demand-fold

Conversation

@beinan

@beinan beinan commented Oct 8, 2026

Copy link
Copy Markdown
Collaborator

Part of #333 (P1.1). Implements docs/design/scheduler-p1-data-model.md §1 (#343).

What

New demand.rs with DemandEvent, ShardDemand, TableDemand and TableDemand::fold. Pure, no etcd. Nothing calls it yet — writers/executors/scanner emission and planner folding come in follow-up PRs; this lands the schema and the invariant tests first.

Fold rules (spec §1.1, review point 3):

  • Higher writer_epoch replaces the shard but keeps any merged watermark already recorded.
  • Lower epoch → Ignored::StaleEpoch.
  • Equal epoch: sealed/merged only move forward; a Scan may raise sealed but lowering → Ignored::ScanCannotLower.
  • Ignored events do not advance observed_revision.

Validation

🤖 Generated with Claude Code

First P1 code, implementing docs/design/scheduler-p1-data-model.md §1.
`demand.rs` defines the `DemandEvent` and `TableDemand` records and
`TableDemand::fold`, a pure function with no etcd dependency. Nothing
calls it yet; later PRs wire writers, executors and the scanner to emit
events and the planner to fold them.

Fold rules: a strictly higher writer epoch replaces the shard but never
discards a merged watermark an executor already recorded; a lower epoch
is ignored; at equal epoch sealed and merged only move forward; a scan
may raise sealed but never lower it (a stale scan must not hide a fresh
flush); ignored events do not advance the observed revision.

Validation: five unit tests. The order-independence test folds every
permutation of a five-event set, with and without each event
duplicated, and replays the final state onto itself, asserting identical
records each time (spec invariant 5a). Others cover the scan epoch rule,
epoch precedence with merged-watermark preservation, aggregates and
revision tracking, and wire-format stability with defaults for optional
fields. Clippy -D warnings, fmt and typos clean.

Refs lance-format#333.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@beinan
beinan merged commit 00e2ee7 into lance-format:main Oct 8, 2026
11 checks passed
beinan added a commit that referenced this pull request Oct 8, 2026
Part of #333 (P1.2). Implements spec §1.1/§1.3 (#343) on top of the fold
from #344.

## What

Writers publish their shard watermark to `P/demand-events/<hex>/<shard>`
on **every** flush, beside the existing `merge-requests` put. A planner
can then see demand below the count threshold, on tables this master
does not own, and for low-count tails — without any sweep.

- **core**: `own_shard_watermark()` reads the writer's own MemWAL
manifest → `(shard_id, writer_epoch, max flushed generation, pending
count)`. Metadata only.
- **merge**: `demand.rs` moves here (shared by server and master).
`Coordinator::publish_demand_event` is a monotonic put ordered by
`(writer_epoch, sealed_through, merged_through)` under a mod-revision
CAS; unchanged watermarks are not rewritten; never deletes.
- **server**: `route_merge` publishes after the drain check and before
the ownership gate; publish failure never blocks the merge request. New
metric
`rollout_wal_demand_events_total{result=written|unchanged|failed}`.

**Additive.** `merge-requests`, count trigger and self-merge paths are
untouched; older masters ignore the key family (review point 5).

## Validation

- `publish_is_monotonic_by_epoch_then_sealed_then_merged` (etcd):
duplicate / lower sealed / lower epoch / merged regress rejected;
sealed, merged, epoch advances and separate shards accepted.
- `flush_publishes_shard_demand_below_threshold_and_without_ownership`
(etcd): threshold 8, 2 pending, non-owned target → no merge request,
exactly one event per shard, folds to the right pending count, unchanged
flush does not bump `mod_revision`, new generation advances
`sealed_through` at the same epoch.
- All 8 sweeper tests, 6 demand tests green against real etcd; clippy
`-D warnings`, fmt, typos clean.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
beinan added a commit that referenced this pull request Oct 8, 2026
#350)

Part of #333 (P1.3). Spec §1.1/§2.2 (#343), on top of #344/#345.

## What

A successful WAL catch-up writes `merged_through` per shard **in the
same etcd txn that releases ownership**, so demand and ownership change
together and a planner never sees a released table whose pending count
still includes the generations just merged.

- **core**: `rollout_append::merged_watermarks(uri)` — per-shard merged
generation from the base table's MemWAL index.
- **merge**: `Coordinator::release_with_demand(claim, execution,
&[ShardMerged])`. `demand_release_changes` only advances
`merged_through`, keeps the writer's `sealed`/`epoch`/`bytes`, creates a
fresh executor-sourced record for an unknown shard, and emits no
compare/op for a shard already at or past the watermark. `release` is
unchanged (delegates with `&[]`).
- **master**: `run_as` reads watermarks after a successful `Catchup` and
releases with them; read failure logs and releases without demand. Other
kinds unchanged. Metric `master_demand_merged_watermarks_total`.

Additive; no key family removed; old masters ignore `demand-events/`.

## Validation

- `release_changes_only_advance_merged_and_keep_writer_fields` (etcd):
advance 15 keeps sealed 20 / epoch 3 / bytes; lower 12 emits nothing;
unknown shard → `sealed == merged`; fold → expected pending.
- `resident_replicas_merge_from_queue_with_no_workers_or_jobs` extended:
after both masters merge, each table has exactly two executor-sourced
events whose `merged_through` equals `merged_watermarks(uri)` and which
fold to `pending_generations == 0`.
- 7 demand tests, 9 master etcd tests (resident + maintenance_execution)
green; clippy `-D warnings`, fmt, typos clean.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
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