-- 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';
