-- Per-lease generation token to close the lease-identity race that -- 0.87.20 only partially addressed. -- -- Before this, `ack_done` / `ack_failed` / `renew` matched on -- `(id, state='running')`. A worker whose lease was released mid- -- flight (force-analyze, reclaim) could still ack-done the row once a -- successor leased it back to `state='running'` — clobbering the -- successor's lease with the original's stale result. The bug class is -- ack-from-a-dead-lease. -- -- Adding `lease_generation` makes lease identity = `(id, generation)`: -- * `lease`/`lease_kinds` bump generation by 1 at lease time -- * `release` (force-analyze, graceful shutdown) bumps generation too -- * `reclaim_orphaned` bumps generation for every reclaimed row -- * `ack_done` / `ack_failed` / `renew` match on `(id, generation, -- state='running')`, so a stale ack from a prior generation finds -- no row and falls back to the existing warn-and-skip branch. -- -- BIGINT so the counter can't realistically overflow even for a row -- that's been crash-leased thousands of times. ALTER TABLE crawler_jobs ADD COLUMN lease_generation BIGINT NOT NULL DEFAULT 0;