tradein: очередь yandex_newbuilding упирается в одни и те же пять домов — неудача ничего не помечает #2924

Closed
opened 2026-08-19 08:13:06 +00:00 by bot-backend · 3 comments
Collaborator

Найдено при разборе #2860. Отдельный дефект: он был не виден, пока резолвер был сломан
целиком, и станет решающим сразу после того, как #2860 поедет.

Что происходит

Выборка домов на обход (_SELECT_PENDING_HOUSES, yandex_newbuilding_sweep.py):

... WHERE hs.ext_source = 'yandex_realty_nb'
      AND NOT EXISTS (SELECT 1 FROM market.yandex_jk_enrichment e WHERE e.ext_id = hs.ext_id)
    ORDER BY h.id, hs.ext_id NULLS LAST
    LIMIT :lim

Из очереди уходит только дом, попавший в витрину. Неудачная попытка не пишет ничего:
ни счётчика попыток, ни времени последней, ни причины. Значит на следующем прогоне
берутся те же самые дома в том же порядке.

Замер 19.08:

ожидающих домов            397
из них уже имеют slug        1
                     без slug 396
первые пять по h.id: 6706, 6741, 6757, 6774, 6785

Это ровно те пять, которые обход обрабатывает каждую неделю с 16.07 — отсюда и
неизменное processed 5, succeeded 0 в четырнадцати прогонах подряд.

Почему это важно именно сейчас

Пока резолвер не работал вообще, порядок был безразличен. После #2860 голова очереди
сдвинется — но любой дом, который не разрешается в принципе (ЖК снят с публикации,
id больше не действителен), заблокирует очередь навсегда, и обход снова встанет,
на этот раз молча и с виду «работая».

Гейт из #2860 (processed > 0 && succeeded == 0 → прогон неуспешен) поймает только
полный отказ. Частичный — «четыре из пяти вечно упираются в мёртвый ЖК» — он пропустит.

Что стоит сделать

  1. Столбец попытки на связке дом↔источник: last_resolve_attempt_at + resolve_attempts
    (или отдельная таблица очереди, если не хочется трогать house_sources).
  2. Выборка сортируется по «давно не пробовали», а не по h.id.
  3. Экспоненциальная отсрочка по числу неудач, с потолком: после N попыток дом уходит в
    конец очереди, а не выпадает из неё насовсем — «снят с публикации» и «сегодня не
    ответило» снаружи неразличимы.
  4. Счётчик в прогоне: сколько домов взято повторно после неудачи — иначе застревание
    снова будет невидимым.

Второй вопрос, к владельцу

limit: 5, interval_days: 7. При очереди в 397 домов это полтора года на один
проход, даже если резолвер работает идеально. Либо частоту/лимит менять, либо признать,
что витрина заполняется медленно и это осознанно.

Оценка: S-M (одна миграция + выборка + счётчик). Refs #2860, #2674.

Найдено при разборе #2860. Отдельный дефект: он был не виден, пока резолвер был сломан целиком, и станет решающим сразу после того, как #2860 поедет. ## Что происходит Выборка домов на обход (`_SELECT_PENDING_HOUSES`, `yandex_newbuilding_sweep.py`): ```sql ... WHERE hs.ext_source = 'yandex_realty_nb' AND NOT EXISTS (SELECT 1 FROM market.yandex_jk_enrichment e WHERE e.ext_id = hs.ext_id) ORDER BY h.id, hs.ext_id NULLS LAST LIMIT :lim ``` Из очереди уходит только дом, **попавший в витрину**. Неудачная попытка не пишет ничего: ни счётчика попыток, ни времени последней, ни причины. Значит на следующем прогоне берутся **те же самые дома в том же порядке**. Замер 19.08: ``` ожидающих домов 397 из них уже имеют slug 1 без slug 396 первые пять по h.id: 6706, 6741, 6757, 6774, 6785 ``` Это ровно те пять, которые обход обрабатывает каждую неделю с 16.07 — отсюда и неизменное `processed 5, succeeded 0` в четырнадцати прогонах подряд. ## Почему это важно именно сейчас Пока резолвер не работал вообще, порядок был безразличен. После #2860 голова очереди сдвинется — но **любой дом, который не разрешается в принципе** (ЖК снят с публикации, id больше не действителен), заблокирует очередь навсегда, и обход снова встанет, на этот раз молча и с виду «работая». Гейт из #2860 (`processed > 0 && succeeded == 0` → прогон неуспешен) поймает только полный отказ. Частичный — «четыре из пяти вечно упираются в мёртвый ЖК» — он пропустит. ## Что стоит сделать 1. Столбец попытки на связке дом↔источник: `last_resolve_attempt_at` + `resolve_attempts` (или отдельная таблица очереди, если не хочется трогать `house_sources`). 2. Выборка сортируется по «давно не пробовали», а не по `h.id`. 3. Экспоненциальная отсрочка по числу неудач, с потолком: после N попыток дом уходит в конец очереди, а не выпадает из неё насовсем — «снят с публикации» и «сегодня не ответило» снаружи неразличимы. 4. Счётчик в прогоне: сколько домов взято повторно после неудачи — иначе застревание снова будет невидимым. ## Второй вопрос, к владельцу `limit: 5`, `interval_days: 7`. При очереди в 397 домов это **полтора года** на один проход, даже если резолвер работает идеально. Либо частоту/лимит менять, либо признать, что витрина заполняется медленно и это осознанно. Оценка: S-M (одна миграция + выборка + счётчик). Refs #2860, #2674.
Author
Collaborator

PR открыт — маркер неудачи + очередь не упирается в те же пять

См. PR выше в треде: миграция 269 , выборка с повтором не раньше 7 дней и неудавшимися в конце очереди, игнорирует маркер, удачный резолв маркер не ставит. Двусторонне проверено против .

Важно для чтения: первые пять домов очереди на выкаченном коде резолвятся (проверено в контейнере), штатный прогон — 24.08 02:03 UTC. Этот дефект вступит в силу на первом неразрешимом доме — теперь он не заблокирует очередь.

## PR открыт — маркер неудачи + очередь не упирается в те же пять См. PR выше в треде: миграция 269 , выборка с повтором не раньше 7 дней и неудавшимися в конце очереди, игнорирует маркер, удачный резолв маркер не ставит. Двусторонне проверено против . Важно для чтения: первые пять домов очереди на выкаченном коде **резолвятся** (проверено в контейнере), штатный прогон — 24.08 02:03 UTC. Этот дефект вступит в силу на первом неразрешимом доме — теперь он не заблокирует очередь.
Author
Collaborator

PR #3017 — маркер неудачи, очередь не упирается в те же пять (дополнение к короткому комментарию выше, в нём часть текста съел shell)

  • Миграция 269houses.yandex_jk_resolve_tried_at timestamptz (nullable, без дефолта; существующие строки = «не пробовали»), по образцу listings.geocode_tried_at (миграция 005).
  • Выборка не берёт дом, у которого попытка свежее RESOLVE_RETRY_DAYS = 7; неудавшиеся уходят в конец очереди (ORDER BY h.id, tried_at NULLS FIRST); force=True маркер игнорирует — ручной полный проход остаётся.
  • Запись tried_at — при неудаче резолва и при падении персиста slug'а, в отдельном SAVEPOINT (потеря отметки не фатальна, падение не роняет прогон). Удачный резолв маркер не ставит.
  • Двусторонне через реальный enrich_yandex_newbuilding_sweep с двойником сессии: против origin/main три теста красные по значению («неудача резолва не помечена; выполненный SQL: […]» — с перечнем реально ушедших запросов), контроль «удачный резолв не ставит маркер» зелёный с обеих сторон. Сюита 4659 passed, гейт нумерации миграций зелёный.

Контекст для чтения: первые пять домов очереди (286394, 2671892, 2367237, 3003941, 609312) на выкаченном коде резолвятся — проверено в контейнере tradein-scraper тем же трактом, что обход. Штатный прогон — 24.08 02:03 UTC. Этот дефект вступит в силу на первом неразрешимом доме; с маркером он больше не заблокирует пять слотов.

## PR #3017 — маркер неудачи, очередь не упирается в те же пять (дополнение к короткому комментарию выше, в нём часть текста съел shell) - **Миграция 269** — `houses.yandex_jk_resolve_tried_at timestamptz` (nullable, без дефолта; существующие строки = «не пробовали»), по образцу `listings.geocode_tried_at` (миграция 005). - **Выборка** не берёт дом, у которого попытка свежее `RESOLVE_RETRY_DAYS = 7`; неудавшиеся уходят в **конец** очереди (`ORDER BY h.id, tried_at NULLS FIRST`); `force=True` маркер игнорирует — ручной полный проход остаётся. - **Запись** `tried_at` — при неудаче резолва и при падении персиста slug'а, в отдельном SAVEPOINT (потеря отметки не фатальна, падение не роняет прогон). Удачный резолв маркер **не** ставит. - **Двусторонне** через реальный `enrich_yandex_newbuilding_sweep` с двойником сессии: против `origin/main` три теста красные по значению («неудача резолва не помечена; выполненный SQL: […]» — с перечнем реально ушедших запросов), контроль «удачный резолв не ставит маркер» зелёный с обеих сторон. Сюита 4659 passed, гейт нумерации миграций зелёный. Контекст для чтения: первые пять домов очереди (`286394, 2671892, 2367237, 3003941, 609312`) на выкаченном коде **резолвятся** — проверено в контейнере `tradein-scraper` тем же трактом, что обход. Штатный прогон — **24.08 02:03 UTC**. Этот дефект вступит в силу на первом неразрешимом доме; с маркером он больше не заблокирует пять слотов.
Author
Collaborator

Закрываю: PR #3017 смержен и проверен на проде

_schema_migrations:  269_houses_yandex_jk_resolve_tried_at.sql | 2026-08-21 09:57:22
houses:              yandex_jk_resolve_tried_at : timestamp with time zone
tradein-scraper:     _mark_resolve_tried / yandex_jk_resolve_tried_at — 7 вхождений в выкаченном коде
контейнеры:          tradein-scraper, tradein-backend пересозданы (Up ~1 min на момент проверки)

Что будет дальше и когда проверяемо: штатный прогон yandex_newbuilding_sweep24.08 02:03 UTC. Первые пять домов очереди резолвятся (проверено в контейнере), поэтому на этом прогоне маркер ещё не сработает. Он сработает на первом неразрешимом доме — и тогда в scrape_runs.counters будет failed_resolve > 0, а у дома появится yandex_jk_resolve_tried_at, и на следующей неделе он не займёт слот. Проверку этого оставляю на #2860 (там критерий «succeeded > 0, витрина сдвинулась»).

## Закрываю: PR #3017 смержен и проверен на проде ``` _schema_migrations: 269_houses_yandex_jk_resolve_tried_at.sql | 2026-08-21 09:57:22 houses: yandex_jk_resolve_tried_at : timestamp with time zone tradein-scraper: _mark_resolve_tried / yandex_jk_resolve_tried_at — 7 вхождений в выкаченном коде контейнеры: tradein-scraper, tradein-backend пересозданы (Up ~1 min на момент проверки) ``` Что будет дальше и когда проверяемо: штатный прогон `yandex_newbuilding_sweep` — **24.08 02:03 UTC**. Первые пять домов очереди резолвятся (проверено в контейнере), поэтому на этом прогоне маркер ещё не сработает. Он сработает на первом неразрешимом доме — и тогда в `scrape_runs.counters` будет `failed_resolve > 0`, а у дома появится `yandex_jk_resolve_tried_at`, и на следующей неделе он не займёт слот. Проверку этого оставляю на #2860 (там критерий «succeeded > 0, витрина сдвинулась»).
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#2924
No description provided.