-- Resume support for kn-API sweeps. -- When a worker dies (redeploy/SIGKILL/OOM), the run row stays 'running'. -- Worker startup hook finds rows with stale heartbeat (>5 min) and re-enqueues -- a resume task that picks up from progress_obj_index using objects_snapshot. -- Idempotent. ALTER TABLE kn_scrape_runs ADD COLUMN IF NOT EXISTS heartbeat_at TIMESTAMPTZ, ADD COLUMN IF NOT EXISTS progress_obj_index INT, ADD COLUMN IF NOT EXISTS total_obj_count INT, ADD COLUMN IF NOT EXISTS objects_snapshot JSONB, ADD COLUMN IF NOT EXISTS params JSONB, ADD COLUMN IF NOT EXISTS resumed_from_run_id BIGINT REFERENCES kn_scrape_runs(run_id); -- Reaper (admin_scrape.py) фильтрует по выражению COALESCE(heartbeat_at, started_at) -- среди status='running'. B-tree по голому heartbeat_at не обслуживает range по -- этому выражению → индекс был мёртв. Индексируем то же выражение и тот же -- статус-предикат, что и в запросе. DROP IF EXISTS — определение индекса -- меняется, а CREATE INDEX IF NOT EXISTS сверяет только имя (см. sql.md). DROP INDEX IF EXISTS idx_kn_scrape_runs_running_heartbeat; CREATE INDEX IF NOT EXISTS idx_kn_scrape_runs_running_heartbeat ON kn_scrape_runs (COALESCE(heartbeat_at, started_at)) WHERE status = 'running';