fix(tradein): прогоны, оборванные деплоем (interrupted=1), невидимы лестницам стриков и honest-status-гейтам (#3393) #3395
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
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: lekss361/gendesign#3395
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "fix/3393-interrupted-runs-outside-streaks"
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?
Closes #3393.
Дефект. После #3392 прерванные деплоем прогоны финализируются
mark_done/mark_failed(раньше —zombie) и попадают в лестницы:_failed_ratio_too_highсудит частичные counters (20 % отказов при обрыве →failedс диагнозом «сбор деградировал» + алерт по 3 подряд), аdonecian-бэкфилла обнуляет стрик банов источника (задокументированный вредorchestration/scheduler.py:90-108).Фикс (одно место на лестницу, обе копии модуля — app
scrape_runs.pyи kitorchestration/runs.py, #3390):_was_interrupted(counters)рядом с_leading_streak;scrape_runs.py:429-464,runs.py:428-463) и сторож zero-result (:481-563/:480-562):rows = [r for r in rows if not _was_interrupted(...)]— прерванный прогон не продлевает и не обнуляет («как будто его не было»;cancelledоставлен прерывателем — там решение человека). Фильтр в Python: сторож подexcept Exception: pass, живого Postgres в тестах нет, SQL-фильтр был бы непроверяем по значению; в SELECT добавлена колонкаcounters,getattr— чтобы отсутствие колонки не выключило сторож молча;mark_done(:691-731/:765-805): три honest-status-гейта пропускаются приinterruptedсlogger.warning("… оборван деплоем (SIGTERM-drain), частичный результат — honest-status-гейты пропущены (#3393)"); колонкаerrorне трогается (машиночитаемая метка есть в counters).Семантика:
failed, failed, failed+interrupted, failed→ стрик 3 (не 4 и не 2);banned, banned, done+interrupted, banned→ 3.Тесты
tests/test_3393_interrupted_runs_outside_streaks.py(параметризованы по обеим копиям) + 4 контроля «те же формы без метки ведут себя как раньше».Фальсификация (
git apply -Rисходников): 10 failed по значению —Expected 'capture_message' to have been called once. Called 0 times,assert ['failed'] == ['done'], в логеfailed-ratio-honest-status … 20 %; контроли зелёные и на main. Восстановлено.Прогоны: полный backend
5544 passed, 35 skipped(rc=0); ruff OK.Приёмка на проде: маркер
_was_interruptedв обеих копиях контейнера scraper (на main символа нет); событийно — после первого деплоя, убившего бегущий прогон: строки сinterrupted='1'безerror LIKE '%failed-ratio-honest-status%', в логахоборван деплоем (SIGTERM-drain) … гейты пропущены (#3393)для run_id изscheduler: drain — … interrupted, и стрик источника (считая без interrupted-строк) совпадает с числом в ближайшем алерте.Code-review (06.09): minor. Подтверждено: обе копии байт-в-байт (кроме кросс-ссылки в докстринге);
interruptedне наследуется при resume (все 7 resume-сайтов pipeline берут толькоdone_buckets, DKP —last_id) → пропуск гейтов вmark_doneне расширяется за пределы дрейна; zero-сторож решает «измерен ли» по свежайшему судимому прогону; рефактор трёх гейтов вor-цепочку эквивалентен (все не-None ветки — непустые f-строки).Реальная находка (докатываю до мержа):
capped = streak >= STREAK_SCAN_LIMITсчитается по отфильтрованному окну — однаinterrupted-строка в 500 делаетcappedнедостижимым (макс. стрик 499), вехи лестницы после 384 идут только на 768 → сломанный наглухо источник после 384-й неудачи молчит навсегда, и каждый деплой подкладывает новую interrupted-строку (класс #2670/#2703). Ревьюер прогнал вживую: 500 failed без interrupted → алерт; с одной interrupted → SILENT. Фикс:scanned = len(rows)до фильтра,capped = scanned >= LIMIT and streak >= len(rows), в обеих копиях + тест-контрпример + контроль без метки для zero-сторожа.