(low) 55_schema_scrape_resume.sql: partial-index на heartbeat_at мёртв — reaper фильтрует по COALESCE(heartbeat_at, started_at) #1400

Closed
opened 2026-06-15 05:52:04 +00:00 by bot-backend · 0 comments
Collaborator

Severity: low · Категория: sql · Файл: data/sql/55_schema_scrape_resume.sql:1-17

/Users/anton/Птица/gendesign/data/sql/55_schema_scrape_resume.sql:15-17 создаёт idx_kn_scrape_runs_running_heartbeat ON kn_scrape_runs (heartbeat_at) WHERE status IN ('running','resuming'). Реальный reaper в backend/app/api/v1/admin_scrape.py:107-117 фильтрует по WHERE status='running' AND finished_at IS NULL AND COALESCE(heartbeat_at, started_at) < NOW() - INTERVAL '10 minutes'. B-tree по голому heartbeat_at не обслуживает range по выражению COALESCE(...) — планировщик его не задействует. Дополнительно рассинхрон с кодом: статус-фильтр индекса включает 'resuming' (в запросе только 'running'), порог в коде 10 мин, а в комментарии миграции (стр.3) 5 мин; startup-hook lifecycle.py:96-97 вообще не смотрит на heartbeat. Импакт практически нулевой: kn_scrape_runs — крошечная ops-таблица (строка на sweep), seq scan дёшев. Как чинить: либо expression-index на COALESCE(heartbeat_at, started_at) WHERE status='running', либо (status, finished_at) WHERE status='running'; синхронизировать комментарий-порог (10 мин) и убрать 'resuming'.

Почему баг: B-tree индекс по голому столбцу heartbeat_at не может обслужить range-предикат по выражению COALESCE(heartbeat_at, started_at) — планировщик его для отбора протухших не задействует, то есть индекс мёртв для своей единственной заявленной цели. Дополнительно посылка миграции рассинхронизирована с кодом: порог в коде 10 минут, а не 5 (стр.3 файла), и используется COALESCE с started_at (legacy-runs без heartbeat). Это «partial index на ложной посылке» из фокус-листа: правильный индекс под существующий запрос — (status, finished_at) WHERE status='running' или expression-index на COALESCE(heartbeat_at, started_at).

Как чинить: Либо привести индекс к реальному запросу (expression index (COALESCE(heartbeat_at, started_at)) WHERE status='running' AND finished_at IS NULL), либо обновить заголовок миграции под фактический порог 10 мин и COALESCE-логику. Текущий индекс по heartbeat_at можно удалить как неиспользуемый (таблица мала — импакт чисто документационно-перформансный, не корректность).


deep-audit «армия ботов 2» · verify+harden, confidence 0.85 · unit U60

**Severity:** low · **Категория:** sql · **Файл:** `data/sql/55_schema_scrape_resume.sql:1-17` /Users/anton/Птица/gendesign/data/sql/55_schema_scrape_resume.sql:15-17 создаёт idx_kn_scrape_runs_running_heartbeat ON kn_scrape_runs (heartbeat_at) WHERE status IN ('running','resuming'). Реальный reaper в backend/app/api/v1/admin_scrape.py:107-117 фильтрует по WHERE status='running' AND finished_at IS NULL AND COALESCE(heartbeat_at, started_at) < NOW() - INTERVAL '10 minutes'. B-tree по голому heartbeat_at не обслуживает range по выражению COALESCE(...) — планировщик его не задействует. Дополнительно рассинхрон с кодом: статус-фильтр индекса включает 'resuming' (в запросе только 'running'), порог в коде 10 мин, а в комментарии миграции (стр.3) 5 мин; startup-hook lifecycle.py:96-97 вообще не смотрит на heartbeat. Импакт практически нулевой: kn_scrape_runs — крошечная ops-таблица (строка на sweep), seq scan дёшев. Как чинить: либо expression-index на COALESCE(heartbeat_at, started_at) WHERE status='running', либо (status, finished_at) WHERE status='running'; синхронизировать комментарий-порог (10 мин) и убрать 'resuming'. **Почему баг:** B-tree индекс по голому столбцу heartbeat_at не может обслужить range-предикат по выражению COALESCE(heartbeat_at, started_at) — планировщик его для отбора протухших не задействует, то есть индекс мёртв для своей единственной заявленной цели. Дополнительно посылка миграции рассинхронизирована с кодом: порог в коде 10 минут, а не 5 (стр.3 файла), и используется COALESCE с started_at (legacy-runs без heartbeat). Это «partial index на ложной посылке» из фокус-листа: правильный индекс под существующий запрос — `(status, finished_at) WHERE status='running'` или expression-index на `COALESCE(heartbeat_at, started_at)`. **Как чинить:** Либо привести индекс к реальному запросу (expression index `(COALESCE(heartbeat_at, started_at)) WHERE status='running' AND finished_at IS NULL`), либо обновить заголовок миграции под фактический порог 10 мин и COALESCE-логику. Текущий индекс по heartbeat_at можно удалить как неиспользуемый (таблица мала — импакт чисто документационно-перформансный, не корректность). --- <sub>deep-audit «армия ботов 2» · verify+harden, confidence 0.85 · unit U60</sub>
bot-backend added the
week ревью 1
label 2026-06-15 05:52:04 +00:00
bot-backend added the
scope/db
label 2026-06-15 11:01:58 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#1400
No description provided.