fix(ptica): осиротевшие прогоны Объектива закрываются, а не висят вечно (#2464) #2978
No reviewers
Labels
No labels
Fable 5 ревью
GG-форсайт
admin
analytics
auth
automation
bug
business
chore
ci
compliance
data
data-moat
docs
duplicate
dx
enhancement
feedback/max
generative
needs-discussion
needs-human
observability
pause-bots
performance
priority/p0
priority/p1
priority/p2
priority/p3
scope/backend
scope/db
scope/devops
scope/frontend
scope/qa
scrapers
security
site-finder
stage/1
stage/2
status/blocked
status/done
status/needs-analysis
status/needs-fix
status/qa
status/ready
status/review
status/wip
tech-debt
tradein
ux
week ревью 1
wontfix
ИРД
вторичка
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: lekss361/gendesign#2978
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "fix/2464-objective-zombie-sweep"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Находка
objective_scrape_runsне подметалась ничем:worker_readyзнал только проkn_scrape_runsиnspd_geo_jobs. На проде 20.08.2026:Шесть прогонов висят «running» 94 дня. У пяти из шести
heartbeat_atзамер в первую минуту, у одного его нет вовсе.Ноль
failedза всю историю — это не удача, а симптом:_finish_run(status='failed')не мог записаться, потому что сессия была отравлена упавшим запросом (причина починена #2972). То есть каждый сбой Объектива исторически превращался не вfailed, а в вечныйrunning.Причина устранена, но жёсткое убийство воркера (редеплой, OOM) по-прежнему оставляет
runningнавсегда: у Объектива нет ни своегоcleanup_zombies, ни снапшота для resume.Правка
Третий блок в
worker_ready, по образцу kn: любая строкаrunningна старте воркера осиротела — активных воркеров в этот момент нет. Resume не ставим, возобновлять нечего.finished_atставится неNOW(), аCOALESCE(heartbeat_at, started_at). Прогон, умерший 94 дня назад, не должен читаться как «завершён только что».Проверка ловушки, которая не подтвердилась
Первым делом я заподозрил, что пометка зомби разгонит монитор свежести — он читает ту же таблицу. Прочитал
_FRESHNESS_SOURCESвadmin_scrape.py, а не предположил:last_success_at,upd_24h/7d,recent_output— все подFILTER (WHERE status = 'done');last_attempt_at = MAX(started_at),last_status— поORDER BY started_at DESC.finished_atзомби-строк не входит ни в один столбец монитора, аstarted_atу шести сирот (17.05) старше последнегоdone(19.08). Монитор не сдвинется ни на йоту. Ловушка была реальной по форме и пустой по существу — это и стоило проверить до правки.Как проверено
origin/mainтри теста красные по существу — «worker_ready не трогает objective_scrape_runs», причём функция при этом отрабатывает 4 запроса. То есть краснота от отсутствующего поведения, а не от отсутствующего символа.WHERE status = 'running'test_only_running_rows_are_touchedfinished_at = NOW()test_finished_at_is_last_sign_of_life_not_now(первый прогон мутации не применился — якорь
WHERE status = 'running'встречается в файле трижды; скрипт упал, а pytest прогнался по неизменённому файлу и напечатал зелёное. Переделал с уникальным якорём.)test_kn_sweep_still_runs— контроль от регресса, зелёный с обеих сторон.pytest backend/tests/workers/— 234 passed.Что проверю на проде после мержа
В отличие от #2975, у этой правки есть наблюдаемый эффект: после деплоя воркер стартует и шесть строк обязаны перейти в
zombieсfinished_at= их собственный последний heartbeat (17.05), а не сегодняшняя дата. Отчитаюсь числами.Часть эпика #2464.
Проверено на проде
Деплой
8ad1e9f5зелёный. В отличие от #2975, здесь эффект наблюдаемый — и он наступил:Причём
finished_at— их собственный последний признак жизни, а не сегодняшняя дата:Прогон, умерший 94 дня назад, не читается как «завершён только что» — ровно то, ради чего
NOW()здесь не годился.Ловушка проверена и на живых данных
Перед правкой я утверждал по коду, что зомби-строки не попадут в монитор свежести ни одним столбцом. Проверил теперь по факту, уже после пометки:
Шесть зомби от 17.05 невидимы монитору полностью. Утверждение из описания PR подтвердилось измерением, а не осталось доводом.
Проверено на проде — эффект наблюдаемый, в отличие от #2975
После деплоя воркер стартовал и подмёл все шесть сирот:
Ключевое —
finished_at= 17.05, а не сегодня. Именно то, ради чего в правке стоитCOALESCE(heartbeat_at, started_at)вместоNOW(): прогон, умерший 94 дня назад, не читаетсякак «завершён только что». Оператор в списке прогонов видит настоящую дату смерти.
Монитор свежести, как и предсказывалось по коду, не сдвинулся:
last_success_atиrecent_outputсчитаются только поstatus='done', аlast_attempt_at/last_status— поstarted_at, поэтому зомби-строки в него не попадают ни одним столбцом.