fix(ptica): на worker_ready зомби помечается ЛЮБОЙ 'running', а не только со снапшотом (#2464) #2975
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#2975
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "fix/2464-zombie-any-running"
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?
Пункт эпика #2464:
lifecycle.py:92.Код противоречил собственному докстрингу
А запрос добавлял
AND objects_snapshot IS NOT NULL. Строка без снапшота в выборку не попадала и оставалась'running'навсегда — ровно то состояние, ради устранения которого функция и заводилась.Почему фильтр там был — и почему его место не здесь
Снапшот действительно нужен:
resume_kn_runвосстанавливает обход «using objects_snapshot» и без него упал бы. Но он нужен для возобновления, а не для пометки.Правка: зомби помечаются все, resume ставится только тем, кого есть чем возобновить; остальные получают честную причину в
errorвместо тишины.Про тест — первая версия была негодной
Двойник сессии отдавал строки независимо от
WHERE. Наorigin/mainглавный тест («строка не помечена») проходил, а краснели два других — по ложной причине: фейк игнорировал фильтр, которого весь спор и касается.Научил двойник соблюдать ровно этот фильтр и сузил совпадение до
AND objects_snapshot IS NOT NULL— правка выносит то же выражение в список полейSELECT, и матч по голой подстроке отсекал бы строки у исправленной версии тоже.Против
origin/mainтеперь:Достижимость
Замер прода 20.08: строк в
'running'сейчас нет — правка предотвращает, а не чинит. Но из 20 исторических'zombie'восемь безobjects_snapshot, так что случай не гипотетический.Прогоны
Докстринг _resume_zombie_runs формулирует инвариант прямо: No time threshold: by definition, on worker_ready ANY 'running' row is a zombie because there is no active worker. Previously we required heartbeat … stayed in 'running' status forever and required manual cancel/resume. А запрос добавлял `AND objects_snapshot IS NOT NULL`. Строка без снапшота в выборку не попадала и оставалась 'running' НАВСЕГДА — ровно то состояние, ради устранения которого функция и заводилась. Снапшот нужен, но не для пометки, а для ВОЗОБНОВЛЕНИЯ: resume_kn_run восстанавливает обход «using objects_snapshot» и без него упал бы. Поэтому зомби помечаются все, а resume ставится только тем, кого есть чем возобновить; остальные получают честную причину в error вместо тишины. Про тест — отдельно, потому что первая версия была негодной. Двойник сессии отдавал строки независимо от WHERE, и на origin/main главный тест («строка не помечена») ПРОХОДИЛ, а краснели два других — по ложной причине. Научил двойник соблюдать ровно тот фильтр, о котором спор, и сузил совпадение до `AND objects_snapshot IS NOT NULL`: правка выносит то же выражение в список полей SELECT, и совпадение по голой подстроке отсекало бы строки у исправленной версии тоже. Против origin/main теперь: строка без снапшота не помечена zombie → падает (UPDATE вообще не выполняется) в смешанной выборке помечены не все → падает: {1,3} вместо {1,2,3} невозобновляемому resume не ставится — контроль, зелёный с обеих сторон возобновляемый получает resume как раньше — контроль, зелёный с обеих сторон Первый контроль ловит «починку», ставящую resume всем подряд. Замер прода 20.08: строк в 'running' сейчас нет, то есть правка предотвращает, а не чинит. Из 20 исторических 'zombie' восемь — без objects_snapshot, так что случай не гипотетический. Прогоны: tests/workers rc=0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>Проверено на проде — и честно о том, что проверить не удалось
Деплой
18593c30→131b83c2зелёный, код в работающем воркере:Инвариант, на котором стоит правка, проверен на проде. Формулировка «на worker_ready ЛЮБОЙ running — зомби, потому что активного воркера нет» верна только при одном воркере, а правка расширила охват пометки. Проверил:
Реплик нет — инвариант держится.
Чего проверить не удалось. Эффекта на проде сейчас не видно: в
kn_scrape_runsнет ни одной строкиrunning(20 zombie, 10 done, 2 failed). Подметать нечего, значит отсутствие сирот после рестарта воркера ничего не доказывает — их не было и до него.Подсадить синтетическую строку я отверг сознательно: пометка ставит
finished_at = NOW(), аkn— критичный источник монитора свежести. Ради проверки испортить показания монитора, который я же чинил, — плохой размен.Критерий с датой: при следующем прерывании kn-прогона (рестарт воркера во время еженедельного понедельничного сбора,
15 4 * * mon) строка обязана оказатьсяzombieс причиной «resume невозможен: нет objects_snapshot», а не остатьсяrunning. Ближайшее окно наблюдения — понедельник 24.08.2026. Если к 25.08 прерываний не случится, критерий переносится, а не считается выполненным.Пока доказано: логика — двусторонним тестом, код — в контейнере, инвариант — на проде. Рантайм-эффект — нет.
Смежная находка по ходу проверки вынесена в #2978:
objective_scrape_runsне подметалась вообще ничем и держит 6 строкrunningс 17.05 (94 суток).