Skip to content

fix: the pending-generation read cap counts every shard, not the worst one - #279

Merged
beinan merged 1 commit into
lance-format:mainfrom
beinan:fix/pending-cap-across-shards
Sep 30, 2026
Merged

beinan merged 1 commit into
lance-format:mainfrom
beinan:fix/pending-cap-across-shards

Conversation

@beinan

@beinan beinan commented Sep 30, 2026

Copy link
Copy Markdown
Collaborator

Problem

The read cap from #278 (pending_generations_max) is checked per writer shard. A read opens every pending generation of every shard, so a store with 20 shards at ~1,400 generations each (25,588 total) passes the check and one point lookup still opens all 25k datasets — it OOMKilled a 32 GiB worker in production an hour after #278 shipped.

Fix

Keep the per-shard check (fails fast, before other shards are listed) and additionally refuse once the collected snapshots' total exceeds the cap, with the same PENDING_GENERATIONS_EXCEEDED prefix the server maps to 503 OVERLOADED. Docs updated to say "per shard or in total".

Verification

pending_generation_cap_counts_every_shard: cap 3, shards with 2+1 generations read fine; 2+2 is refused with "across 2 shards"; merging one shard brings reads back. Existing per-shard test unchanged; core lib suite passes.

🤖 Generated with Claude Code

…t one

A read opens every pending generation of every writer shard, so the cap
introduced in lance-format#278 must hold for the store as a whole. A store with 20
shards at ~1,400 generations each (25,588 total) passed the per-shard
check and one point lookup opened all of them, OOMKilling the worker.

The per-shard check stays (it fails fast, before the other shards are
even listed); the total is checked once the snapshots are collected.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@beinan
beinan merged commit 773ce8f into lance-format:main Sep 30, 2026
10 checks passed
beinan added a commit that referenced this pull request Sep 30, 2026
…on is unchanged (#281)

## Problem

The scanner's unchanged-version shortcut reuses the previous stats row
wholesale. WAL flushes never bump the base-table version, so a store
that no worker or master has ever merged keeps its version forever while
its shards fill up. In production `mai3_bigclimb_run6p5_77b_t0r1` sat at
"3 pending" in the stats table while its 20 shards accumulated 17,254
generations; the merge sweep reads that row, so it never scheduled a
merge, and after #278/#279 every read on the store was refused (503) by
the pending-generation cap.

## Fix

Both `observe_generic` and `observe_one` still skip the base-table row
count on an unchanged version, but recount `pending_wal_generations`
(one small shard-manifest read per shard, under the existing observe
timeout) and write it into the reused row.

## Verification

- `skipped_round_recounts_pending_generations`: 1 → 4 flushed
generations with the base version unchanged; the skipped row reports 4.
- `generic_store_is_observed_with_pending_wal` extended the same way (3
→ 5).
- Master suite incl. etcd `--include-ignored` passes; clippy 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