docs(scraper-kit): обоснование #3319 — что именно теряло чекпоинт
All checks were successful
CI Trade-In / changes (pull_request) Successful in 11s
CI / changes (pull_request) Successful in 13s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 5m13s
All checks were successful
CI Trade-In / changes (pull_request) Successful in 11s
CI / changes (pull_request) Successful in 13s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 5m13s
Ревью опровергло формулировку «точку терял каждый финализатор»: все четыре писателя в runs.py мержат jsonb (`counters || :counters`), записанный ключ переживал mark_done/mark_banned/mark_failed. Правка закрывает выходы РАНЬШЕ первого end-of-anchor heartbeat (cancel/дрейн на первом якоре, ранний done #1950 на якоре №1) — комментарий и докстринг переписаны на это. Замер «0 из 67 за 60 дней» назван тем, чем он является: запись появилась 26.08.2026 (#3074) при такте avito 7 суток, выборка почти вся из эры без механизма. Плюс тест на прогон без ключей (эра до #3074) — метка дрейна не меняет вердикт «нечего подхватывать».
This commit is contained in:
parent
0ed934ea09
commit
41f21c4969
2 changed files with 29 additions and 12 deletions
|
|
@ -1,11 +1,15 @@
|
|||
"""Чекпоинт avito_city_sweep доживает до финализатора (#3319).
|
||||
|
||||
Прод-факт, из которого выросла задача: 0 из 67 прогонов за 60 дней имеют в
|
||||
counters ключ done_buckets. Механизм #3074 (запись точки) и механизм #930
|
||||
(подхват точки) существуют оба, но между ними нет ни одного прогона: точку
|
||||
писала ровно одна строка внутри цикла якорей, а КАЖДЫЙ выход из прогона
|
||||
(mark_done — включая ранний выход #1950 «SERP собран, detail заблокирован», —
|
||||
mark_banned, mark_failed) отдавал голый counters.to_dict() без неё.
|
||||
Точку писала ровно одна строка — end-of-anchor heartbeat в конце итерации цикла
|
||||
якорей. Финализаторы её не стирали (все писатели в runs.py мержат jsonb:
|
||||
`counters || :counters`), дыра в другом: выходы, случившиеся РАНЬШЕ первой такой
|
||||
записи, точки не оставляли вовсе — cancel/SIGTERM-дрейн на границе первого якоря
|
||||
и ранний done #1950 («SERP собран, detail заблокирован») на якоре №1. Ими и
|
||||
кончается типичный прод-прогон с `anchors_done: 1` из 5.
|
||||
|
||||
Замер «0 из 67 прогонов за 60 дней несут done_buckets» тут НЕ доказательство:
|
||||
строка записи появилась только 26.08.2026 (#3074) при такте avito 7 суток —
|
||||
выборка почти целиком из эры, где механизма не существовало.
|
||||
|
||||
Три инварианта, ради которых тест:
|
||||
1. done-выход несёт done_buckets — иначе точка существует только в логе.
|
||||
|
|
@ -212,3 +216,10 @@ def test_drained_done_is_resumable_but_clean_done_is_not() -> None:
|
|||
resume_from, verdict = _resume_decision(_prev_run(ckpt))
|
||||
assert resume_from is None, "полный обход подхватывать нечего"
|
||||
assert verdict["resume_reason"] == "status_done"
|
||||
|
||||
# Прогон из эры до #3074: ключей нет вовсе — метка дрейна не должна менять
|
||||
# вердикт «нечего подхватывать» на что-то другое.
|
||||
_, verdict = _resume_decision(_prev_run({}))
|
||||
assert verdict["resume_reason"] == "status_done"
|
||||
_, verdict = _resume_decision(_prev_run({"interrupted": 1}))
|
||||
assert verdict["resume_reason"] == "no_checkpoint"
|
||||
|
|
|
|||
|
|
@ -1186,12 +1186,18 @@ async def run_avito_city_sweep(
|
|||
def _ckpt(**extra: Any) -> dict[str, Any]:
|
||||
"""Счётчики прогона ВМЕСТЕ с чекпоинтом — payload любого выхода (#3319).
|
||||
|
||||
До этого точку писала ровно одна строка внутри цикла якорей, а все
|
||||
финализаторы (mark_done/mark_banned/mark_failed, включая ранний выход
|
||||
#1950 «SERP OK, detail заблокирован») отдавали голый `counters.to_dict()`.
|
||||
Точка держалась исключительно на jsonb-мерже в runs.py — на свойстве
|
||||
ЧУЖОГО модуля, которого этот файл ничем не проверяет; выход, случившийся
|
||||
раньше первой записи (или писатель без мержа), терял её молча.
|
||||
До этого точку писала ровно одна строка — end-of-anchor heartbeat в конце
|
||||
итерации цикла. Финализаторы её НЕ стирали: все четыре писателя в runs.py
|
||||
мержат jsonb (`counters || :counters`), уже записанный ключ переживал и
|
||||
mark_done, и mark_banned, и mark_failed. Закрывается другая дыра — выходы,
|
||||
случившиеся РАНЬШЕ первой такой записи: cancel/SIGTERM-дрейн на границе
|
||||
первого якоря и ранний done #1950 («SERP собран, detail заблокирован») на
|
||||
якоре №1. Именно им и кончается типичный прод-прогон, у которого
|
||||
`anchors_done: 1` из 5.
|
||||
|
||||
Замер «0 из 67 прогонов за 60 дней несут done_buckets» сам по себе этого НЕ
|
||||
доказывает: строка записи появилась только 26.08.2026 (#3074), а такт avito
|
||||
— 7 суток, так что выборка почти целиком из эры, где механизма не было.
|
||||
"""
|
||||
return {**counters.to_dict(), "done_buckets": sorted(_done_anchors), **extra}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue