tradein/avito: серия отказов НАШЕГО тракта не рвёт detail-бэкфилл — 4 прогона × 1600 попыток дали 8 карточек и статус done (infra провалился между ratio-брейк… #3439

Open
opened 2026-09-11 11:32:45 +00:00 by bot-backend · 0 comments
Collaborator

Симптом (прод, 10.09 16:33 → 11.09 00:00). Четыре прогона avito_detail_backfill подряд взяли по 1600 карточек и обогатили 8 суммарно — и все четыре завершились done, без единого алерта:

run attempted enriched blocked proxy_ids status
6608 1600 2 1594 [15] done
6631 1600 1 1599 [14] done
6643 1600 4 1595 [13] done
6662 1600 1 1599 [15] done

В это же окно сайдкар: 3259 browser launch failed из 3260 запусков (exc_type=InvalidIP), 3188 ответов /fetch 503, tradein-browser на 106 % CPU и OOMKilled=true. То есть площадка нас не блокировала — лежал наш тракт, а прогоны продолжали жечь снапшот до конца.

Почему предохранители не сработали — по коду, а не по догадке.

  1. app/services/backfill_block_breaker.py:110-112record_block(kind) при kind != 'platform' делегирует в record_failure() (правка #3288: infra не должен объявлять бан площадки).
  2. :123-126record_failure() делает ровно self._window.append(False): в знаменатель окна. Счётчика серии отказов там нет вовсе.
  3. ratio-критерий (:159) считает window_blocks / window_len, где числитель растёт только на platform → при чисто infra-потоке числитель ноль по построению.
  4. safety-net (abort_reason, :184-188) смотрит на _consecutive_blocks — тоже только platform.

Итог: серия infra-отказов любой длины не может оборвать прогон — ни ratio, ни safety-net, ни «N подряд» (такого критерия нет; max_consecutive_failures=25 из докстринга в брейкере не реализован). Лог честно печатал BLOCKED #1600/1600 (consecutive=5) — счётчик стоял на месте все 1600 попыток.

  1. Финализация #3367: dominant infra ∧ produced > 0 → mark_done. Условие «хоть что-то принёс» выполняется одной карточкой из 1600 → done, свежесть обновлена, лестница алертов молчит.

Это регресс моих же правок #3288/#3367: уводя infra-отказ из «бана площадки», я не оставил ему никакого своего предохранителя — он провалился между ratio (только platform) и failures (счётчика нет).

Что делать (предложение, требует ревью):

  • отдельный счётчик серии infra-отказов в BlockRatioBreaker + порог (настройка, дефолт ~15-20) → обрыв с abort_reason="infra_streak"; статус прогона failed с честным текстом («тракт недоступен: N отказов сайдкара подряд»), НЕ banned — площадка молчала (инвариант #3196/#2764 цел);
  • mark_backfill_finished: produced > 0 — слишком слабое условие для done; при доминирующем infra и доле успеха ниже порога статус должен быть failed;
  • узел, на аренде которого сайдкар не смог поднять браузер, должен получать отрицательный браузерный вердикт (browser_unfit), а не оставаться в выдаче: за 14 часов шторма proxy_healthcheck дал browser_checked: 0 почти во всех прогонах (проба гоняется по browser_probe_minutes).

Тесты по значению (обязательны): серия infra-отказов + 1 успех → прогон оборван на пороге, статус failed; здоровый прогон с 25 % платформенных блоков не рвётся; прежний контроль #3367 (infra при нормальной доле успеха → done) остаётся зелёным.

Приёмка на проде: следующий шторм сайдкара обязан оборвать прогон в пределах порога (attempted ≈ порог, не 1600), статус failed с abort_reason=infra_streak; суточный расход попыток по avito падает кратно.

Refs #3288, #3367, #3184, #2764, #3196.

**Симптом (прод, 10.09 16:33 → 11.09 00:00).** Четыре прогона `avito_detail_backfill` подряд взяли по 1600 карточек и обогатили 8 суммарно — и **все четыре завершились `done`**, без единого алерта: | run | attempted | enriched | blocked | proxy_ids | status | |---|---:|---:|---:|---|---| | 6608 | 1600 | 2 | 1594 | [15] | done | | 6631 | 1600 | 1 | 1599 | [14] | done | | 6643 | 1600 | 4 | 1595 | [13] | done | | 6662 | 1600 | 1 | 1599 | [15] | done | В это же окно сайдкар: **3259 `browser launch failed` из 3260 запусков** (`exc_type=InvalidIP`), 3188 ответов `/fetch 503`, `tradein-browser` на 106 % CPU и `OOMKilled=true`. То есть площадка нас не блокировала — лежал наш тракт, а прогоны продолжали жечь снапшот до конца. **Почему предохранители не сработали — по коду, а не по догадке.** 1. `app/services/backfill_block_breaker.py:110-112` — `record_block(kind)` при `kind != 'platform'` делегирует в `record_failure()` (правка #3288: infra не должен объявлять бан площадки). 2. `:123-126` — `record_failure()` делает ровно `self._window.append(False)`: в **знаменатель** окна. Счётчика серии отказов там нет вовсе. 3. ratio-критерий (`:159`) считает `window_blocks / window_len`, где числитель растёт только на `platform` → при чисто infra-потоке числитель **ноль по построению**. 4. safety-net (`abort_reason`, `:184-188`) смотрит на `_consecutive_blocks` — тоже только platform. Итог: **серия infra-отказов любой длины не может оборвать прогон** — ни ratio, ни safety-net, ни «N подряд» (такого критерия нет; `max_consecutive_failures=25` из докстринга в брейкере не реализован). Лог честно печатал `BLOCKED #1600/1600 (consecutive=5)` — счётчик стоял на месте все 1600 попыток. 5. Финализация #3367: `dominant infra ∧ produced > 0 → mark_done`. Условие «хоть что-то принёс» выполняется одной карточкой из 1600 → `done`, свежесть обновлена, лестница алертов молчит. **Это регресс моих же правок #3288/#3367:** уводя infra-отказ из «бана площадки», я не оставил ему никакого своего предохранителя — он провалился между ratio (только platform) и failures (счётчика нет). **Что делать (предложение, требует ревью):** - отдельный счётчик серии infra-отказов в `BlockRatioBreaker` + порог (настройка, дефолт ~15-20) → обрыв с `abort_reason="infra_streak"`; статус прогона **`failed`** с честным текстом («тракт недоступен: N отказов сайдкара подряд»), НЕ `banned` — площадка молчала (инвариант #3196/#2764 цел); - `mark_backfill_finished`: `produced > 0` — слишком слабое условие для `done`; при доминирующем infra и доле успеха ниже порога статус должен быть `failed`; - узел, на аренде которого сайдкар не смог поднять браузер, должен получать отрицательный браузерный вердикт (`browser_unfit`), а не оставаться в выдаче: за 14 часов шторма `proxy_healthcheck` дал `browser_checked: 0` почти во всех прогонах (проба гоняется по `browser_probe_minutes`). **Тесты по значению (обязательны):** серия infra-отказов + 1 успех → прогон оборван на пороге, статус `failed`; здоровый прогон с 25 % платформенных блоков не рвётся; прежний контроль #3367 (infra при нормальной доле успеха → `done`) остаётся зелёным. **Приёмка на проде:** следующий шторм сайдкара обязан оборвать прогон в пределах порога (attempted ≈ порог, не 1600), статус `failed` с `abort_reason=infra_streak`; суточный расход попыток по avito падает кратно. Refs #3288, #3367, #3184, #2764, #3196.
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#3439
No description provided.