tradein/scraper: ban_kind проставляется по умолчанию у 9 вызывающих из 12 — метка выглядит доказательством, не будучи им #2764

Closed
opened 2026-08-06 22:49:07 +00:00 by bot-backend · 2 comments
Collaborator

Найдено при разборе нулевого обогащения Авито (#2695) и подтверждено замером. Вчерашняя правка #2711 развела «наш сбой» и «блокировка площадкой» — но новая метка проставляется по умолчанию у большинства вызывающих, то есть выглядит доказательством, не будучи им.

Замер

mark_banned вызывается из двенадцати с лишним мест в конвейере. Диагноз по типу исключения (ban_kind_of_exception) передают три:

pipeline.py:1573  ban_kind=ban_kind_of_exception(e)
pipeline.py:1783  ban_kind=ban_kind_of_exception(e)
pipeline.py:3652  ban_kind=ban_kind_of_exception(exc)

Все остальные получают сигнатурный дефолт BAN_KIND_PLATFORM.

Что из этого вышло на практике — оба прогона со статусом «забанен» после применения миграции:

avito_detail_backfill     12:40  ban_kind=platform   ← дефолт, причина НЕ установлена
domclick_detail_backfill  15:17  ban_kind=platform   ← дефолт, причина известна и правда внешняя

Оба пришли из общего финализатора backfill-задач (#2695), который диагноз не передаёт. Для Домклика «площадка» случайно верна. Для Авито причина 1600 отказов осталась неустановленной — и всё равно записана как блокировка площадкой.

Почему это подрывает вчерашнюю работу

Смысл разведения был в том, чтобы не принимать решения по врущей метке: 92 прогона из 115 были помечены «нас забанили», а на деле отказывал наш собственный сайдкар, и на этом основании сбор замедлили вдвое.

Ретро-классификация исторических строк сделана по тексту ошибки и потому осмысленна. Новые строки осмысленны только у трёх вызывающих из двенадцати. Со временем доля «доказанных» меток будет падать, а выглядеть таблица будет так же уверенно.

Это тот же класс, что и сам дефект #2686, только на уровень выше: раньше метка была слишком широкой, теперь она проставляется без основания.

Что предлагается

Убрать дефолт platform. Два варианта, оба лучше нынешнего:

  1. Сделать ban_kind обязательным параметром — каждый вызывающий обязан решить. Дорого по диффу, зато забыть нельзя.
  2. Дефолт unknown — честно, и разрыв становится виден в данных, а не прячется. platform и infra тогда означают именно доказательство.

Склоняюсь ко второму: он делает пробел измеримым (SELECT ban_kind, count(*) сразу покажет, сколько мы на самом деле знаем), а первый превращает правку в обход двенадцати мест ради строк, которые всё равно будут скопированы бездумно.

Плюс, независимо от выбора: в браузерном режиме fetch_detail превращает в «блокировку площадкой» любой отказ сайдкара, — значит и там, где диагноз передаётся, он может быть неверен. Проверить эту ветку отдельно.

Что НЕ делать

Не выводить диагноз из текста ошибки в рантайме. В #2711 это сознательно сделано по типу исключения, а разбор текста постфактум — ровно то, от чего уходили.

Связано: #2686, #2711, #2695, #2674.

Найдено при разборе нулевого обогащения Авито (#2695) и подтверждено замером. Вчерашняя правка #2711 развела «наш сбой» и «блокировка площадкой» — но новая метка **проставляется по умолчанию у большинства вызывающих**, то есть выглядит доказательством, не будучи им. ## Замер `mark_banned` вызывается из **двенадцати с лишним мест** в конвейере. Диагноз по типу исключения (`ban_kind_of_exception`) передают **три**: ``` pipeline.py:1573 ban_kind=ban_kind_of_exception(e) pipeline.py:1783 ban_kind=ban_kind_of_exception(e) pipeline.py:3652 ban_kind=ban_kind_of_exception(exc) ``` Все остальные получают сигнатурный дефолт `BAN_KIND_PLATFORM`. Что из этого вышло на практике — оба прогона со статусом «забанен» после применения миграции: ``` avito_detail_backfill 12:40 ban_kind=platform ← дефолт, причина НЕ установлена domclick_detail_backfill 15:17 ban_kind=platform ← дефолт, причина известна и правда внешняя ``` Оба пришли из общего финализатора backfill-задач (#2695), который диагноз не передаёт. Для Домклика «площадка» случайно верна. Для Авито **причина 1600 отказов осталась неустановленной** — и всё равно записана как блокировка площадкой. ## Почему это подрывает вчерашнюю работу Смысл разведения был в том, чтобы не принимать решения по врущей метке: 92 прогона из 115 были помечены «нас забанили», а на деле отказывал наш собственный сайдкар, и на этом основании сбор замедлили вдвое. Ретро-классификация исторических строк сделана по тексту ошибки и потому осмысленна. **Новые строки осмысленны только у трёх вызывающих из двенадцати.** Со временем доля «доказанных» меток будет падать, а выглядеть таблица будет так же уверенно. Это тот же класс, что и сам дефект #2686, только на уровень выше: раньше метка была слишком широкой, теперь она **проставляется без основания**. ## Что предлагается **Убрать дефолт `platform`.** Два варианта, оба лучше нынешнего: 1. Сделать `ban_kind` обязательным параметром — каждый вызывающий обязан решить. Дорого по диффу, зато забыть нельзя. 2. **Дефолт `unknown`** — честно, и разрыв становится виден в данных, а не прячется. `platform` и `infra` тогда означают именно доказательство. Склоняюсь ко второму: он делает пробел измеримым (`SELECT ban_kind, count(*)` сразу покажет, сколько мы на самом деле знаем), а первый превращает правку в обход двенадцати мест ради строк, которые всё равно будут скопированы бездумно. Плюс, независимо от выбора: в браузерном режиме `fetch_detail` превращает в «блокировку площадкой» **любой** отказ сайдкара, — значит и там, где диагноз передаётся, он может быть неверен. Проверить эту ветку отдельно. ## Что НЕ делать Не выводить диагноз из текста ошибки в рантайме. В #2711 это сознательно сделано по **типу исключения**, а разбор текста постфактум — ровно то, от чего уходили. Связано: #2686, #2711, #2695, #2674.
Author
Collaborator

Работаю над этим в PR #2765.

Работаю над этим в PR #2765.
Author
Collaborator

ЗАКРЫТО — метка перестала назначаться по умолчанию, проверка на проде 2026-08-07 09:0x UTC

Дефолт снят — в живом контейнере, а не в main

tradein-scraper: app/services/scrape_runs.py:524   ban_kind: str = BAN_KIND_UNKNOWN
tradein-postgres: миграция 234 применена 2026-08-06 23:24:42
                  CHECK: ban_kind IN ('platform','infra','unknown')

Выбран вариант 2 из постановки — разрыв стал измеримым, а не спрятанным.

Историю переразметили честно

Оба прогона, которые в теле задачи несли platform по умолчанию, теперь читаются как unknown:

3306  avito_detail_backfill     12:40  platform → unknown
3313  domclick_detail_backfill  15:17  platform → unknown

Для Домклика «площадка» была случайно верна — и всё равно переписана в unknown, потому что доказательства не было. Это правильный размен: метка теперь означает доказательство, а не совпадение.

Первый забаненный прогон на новом коде — метка ДОКАЗАНА

3368  avito_detail_backfill  2026-08-07 07:55:17 → 07:57:26
      status=banned  ban_kind=platform
      counters: attempted 5 · blocked 5 · enriched 0 · failed 0

platform здесь пришёл не из сигнатуры: общий финализатор собирает диагнозы всех блоков через ban_kind_of_exception и схлопывает их сам — сошлись в одном (kinds.pop() if len(kinds) == 1), разошлись или ничего не доказывают → unknown. Пять блоков сошлись.

Симметрично: domclick_detail_backfill типы не различает, и это записано ограничением прямо в его docstring — значит его следующий бан придёт как unknown, а не как уверенная ложь.

Отдельная ветка из постановки — тоже закрыта

«В браузерном режиме fetch_detail превращает в блокировку площадкой ЛЮБОЙ отказ сайдкара» — это чинилось, а не оставалось на потом. В живом контейнере providers/avito/detail.py:420 вместо AvitoBlockedError теперь AvitoSidecarUnavailableError, и зеркальная SERP-ветка (#2686) помечает те же отказы infra. То есть три вызывающих, передающих диагноз, больше не могут передать его неверно на этом пути.

PR: #2765.

## ЗАКРЫТО — метка перестала назначаться по умолчанию, проверка на проде 2026-08-07 09:0x UTC ### Дефолт снят — в живом контейнере, а не в main ``` tradein-scraper: app/services/scrape_runs.py:524 ban_kind: str = BAN_KIND_UNKNOWN tradein-postgres: миграция 234 применена 2026-08-06 23:24:42 CHECK: ban_kind IN ('platform','infra','unknown') ``` Выбран вариант 2 из постановки — разрыв стал измеримым, а не спрятанным. ### Историю переразметили честно Оба прогона, которые в теле задачи несли `platform` по умолчанию, теперь читаются как `unknown`: ``` 3306 avito_detail_backfill 12:40 platform → unknown 3313 domclick_detail_backfill 15:17 platform → unknown ``` Для Домклика «площадка» была случайно верна — и всё равно переписана в `unknown`, потому что доказательства не было. Это правильный размен: метка теперь означает доказательство, а не совпадение. ### Первый забаненный прогон на новом коде — метка ДОКАЗАНА ``` 3368 avito_detail_backfill 2026-08-07 07:55:17 → 07:57:26 status=banned ban_kind=platform counters: attempted 5 · blocked 5 · enriched 0 · failed 0 ``` `platform` здесь пришёл не из сигнатуры: общий финализатор собирает диагнозы **всех** блоков через `ban_kind_of_exception` и схлопывает их сам — сошлись в одном (`kinds.pop() if len(kinds) == 1`), разошлись или ничего не доказывают → `unknown`. Пять блоков сошлись. Симметрично: `domclick_detail_backfill` типы не различает, и это записано ограничением прямо в его docstring — значит его следующий бан придёт как `unknown`, а не как уверенная ложь. ### Отдельная ветка из постановки — тоже закрыта «В браузерном режиме `fetch_detail` превращает в блокировку площадкой ЛЮБОЙ отказ сайдкара» — это чинилось, а не оставалось на потом. В живом контейнере `providers/avito/detail.py:420` вместо `AvitoBlockedError` теперь `AvitoSidecarUnavailableError`, и зеркальная SERP-ветка (#2686) помечает те же отказы `infra`. То есть три вызывающих, передающих диагноз, больше не могут передать его неверно на этом пути. PR: #2765.
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#2764
No description provided.