fix(tradein/imv): «временная» ошибка снова временная — 1390 домов возвращаются в очередь #2843

Merged
bot-backend merged 1 commit from fix/house-imv-retry-stuck into main 2026-08-12 16:06:25 +00:00
Collaborator

Что было

Очередь домовой IMV-оценки выбирает ровно один imv_status за прогон (only_status, дефолт 'pending'). Повтор существовал только как значение этого параметра и был задокументирован в admin-API — но за всю историю (41 прогон, 26.06–11.08) его не передал ни один вызов. Дома, упавшие по инфраструктурной причине, которой больше нет (503/500 от браузерного сайдкара, #2698), лежали в transient_error навсегда.

Перемерено на проде 12.08.2026:

transient_error 1390 (адрес есть у 1390, параметры у 1390)
причина 1304 сайдкар · 26 «All connection attempts failed» · 38 auth-403 · 22 прочее
прогонов house_imv_backfill 41, все с only_status='pending'
до площадки доходило 17 домов из 50 (состав головы очереди)

Сайдкар жив (проверено его же access-логом, а не нашими датами отказов): за 7 суток до 12.08 POST /fetch-json108 из 108 = 200, GET /health сейчас {"status": "ok"} со всеми четырьмя браузерами. Прогон 11.08: 25 сохранено из 27 попыток.

Что сделано

  1. Отдельная retry-очередь на половине пакета. Не OR к основной выборке: у pending last_imv_attempt_at всегда NULL, поэтому при общем ORDER BY ... NULLS FIRST повтор ждал бы опустошения pending (≈год). Явный only_status автоповтор отключает — ручной запрос оператора обрабатывает ровно запрошенный статус, включая исчерпавшие лимит дома.

  2. Выход из повтора — счётчик imv_transient_attempts (миграция 258; в теле коммита ошибочно «257» — этот номер занял чужой мерж #2840, пока шла работа). Растёт только на transient_error, обнуляется успехом, порог 3. Число из замера: после починки сайдкара доля отказов на попытку 2/27 и 3/25 ≈ 10% → порог стоит максимум ~115 слотов ВСЕГО. Статус не переименован: «застряли окончательно» = transient_error AND imv_transient_attempts >= 3.

  3. Половина пакета отдана повтору не за счёт pending. Дома, по которым запрос к площадке невозможен, помечаются no_params одним запросом ДО пакета — тот же вердикт и те же строки причин, что у поштучного пути. На проде это 2276 домов из 6533 в очередях (113 без объявлений с rooms+area, 2163 без house_type); поштучно на них ушло бы 45 прогонов расписания ≈ 136 суток.

Пропускная способность (пакет 50 / 3 суток)

сейчас после
доходит до площадки за прогон 17 из 50 50 из 50
pending опустеет 103 такта ≈ 309 сут ~88 тактов ≈ 264 сут
1390 застрявших опустеют никогда ~47 тактов ≈ 141 сут

Цена: запросов к Авито за прогон 17→50 при той же паузе 5 с, длительность прогона ~18 мин → ~30 мин.

Test plan

  • tests/test_house_imv_retry_stuck.py — живой Postgres со схемой прода. На origin/main (тот же файл, та же БД, колонка на месте) падает по существу:
    AssertionError: дом с временной ошибкой и непотраченным лимитом обязан попасть в пакет обычного прогона; фактически статус 'transient_error' (в пакет прогона попало домов: 2)
    На ветке — 4 passed.
  • Полный сьют tradein против живой БД: 4308 passed, 1 skipped.
  • ruff check / ruff format чисто, scripts/check-migration-lock-timeout.py — ок, миграция ре-применяется идемпотентно.

Критерий приёмки на проде (записан ДО факта)

Следующий прогон по расписанию — 2026-08-14 15:59 UTC. После него:

  1. scrape_runs (source=house_imv_backfill, started_at 14.08): counters->>'retried' = 25, counters->>'premarked'2200.
  2. transient_error ≤ 1020 (было 1390: −351 премарк, −25 повтор, плюс новые отказы прогона).
  3. no_params2500 (было 385).
  4. ok вырос ≥ 30 (было 1675).
  5. Провал: retried=0 или premarked=0 → код не доехал; смотреть КОД в контейнере, не SENTRY_RELEASE.

Refs #2674

## Что было Очередь домовой IMV-оценки выбирает **ровно один** `imv_status` за прогон (`only_status`, дефолт `'pending'`). Повтор существовал только как значение этого параметра и был задокументирован в admin-API — но за всю историю (41 прогон, 26.06–11.08) его не передал ни один вызов. Дома, упавшие по инфраструктурной причине, которой больше нет (503/500 от браузерного сайдкара, #2698), лежали в `transient_error` **навсегда**. Перемерено на проде 12.08.2026: | | | |---|---| | `transient_error` | **1390** (адрес есть у 1390, параметры у 1390) | | причина | 1304 сайдкар · 26 «All connection attempts failed» · 38 auth-403 · 22 прочее | | прогонов `house_imv_backfill` | 41, **все** с `only_status='pending'` | | до площадки доходило | **17 домов из 50** (состав головы очереди) | **Сайдкар жив** (проверено его же access-логом, а не нашими датами отказов): за 7 суток до 12.08 `POST /fetch-json` — **108 из 108 = 200**, `GET /health` сейчас `{"status": "ok"}` со всеми четырьмя браузерами. Прогон 11.08: 25 сохранено из 27 попыток. ## Что сделано 1. **Отдельная retry-очередь на половине пакета.** Не `OR` к основной выборке: у `pending` `last_imv_attempt_at` всегда NULL, поэтому при общем `ORDER BY ... NULLS FIRST` повтор ждал бы опустошения pending (≈год). Явный `only_status` автоповтор отключает — ручной запрос оператора обрабатывает ровно запрошенный статус, включая исчерпавшие лимит дома. 2. **Выход из повтора — счётчик** `imv_transient_attempts` (миграция **258**; в теле коммита ошибочно «257» — этот номер занял чужой мерж #2840, пока шла работа). Растёт только на `transient_error`, обнуляется успехом, порог 3. Число из замера: после починки сайдкара доля отказов на попытку 2/27 и 3/25 ≈ 10% → порог стоит максимум ~115 слотов ВСЕГО. Статус не переименован: «застряли окончательно» = `transient_error AND imv_transient_attempts >= 3`. 3. **Половина пакета отдана повтору не за счёт pending.** Дома, по которым запрос к площадке невозможен, помечаются `no_params` одним запросом ДО пакета — тот же вердикт и те же строки причин, что у поштучного пути. На проде это **2276** домов из 6533 в очередях (113 без объявлений с rooms+area, 2163 без house_type); поштучно на них ушло бы **45 прогонов расписания ≈ 136 суток**. ## Пропускная способность (пакет 50 / 3 суток) | | сейчас | после | |---|---|---| | доходит до площадки за прогон | 17 из 50 | 50 из 50 | | pending опустеет | 103 такта ≈ 309 сут | ~88 тактов ≈ 264 сут | | 1390 застрявших опустеют | **никогда** | ~47 тактов ≈ 141 сут | Цена: запросов к Авито за прогон 17→50 при той же паузе 5 с, длительность прогона ~18 мин → ~30 мин. ## Test plan - [x] `tests/test_house_imv_retry_stuck.py` — живой Postgres со схемой прода. На `origin/main` (тот же файл, та же БД, колонка на месте) падает **по существу**: `AssertionError: дом с временной ошибкой и непотраченным лимитом обязан попасть в пакет обычного прогона; фактически статус 'transient_error' (в пакет прогона попало домов: 2)` На ветке — 4 passed. - [x] Полный сьют tradein против живой БД: **4308 passed, 1 skipped**. - [x] `ruff check` / `ruff format` чисто, `scripts/check-migration-lock-timeout.py` — ок, миграция ре-применяется идемпотентно. ## Критерий приёмки на проде (записан ДО факта) Следующий прогон по расписанию — **2026-08-14 15:59 UTC**. После него: 1. `scrape_runs` (source=`house_imv_backfill`, started_at 14.08): `counters->>'retried'` = **25**, `counters->>'premarked'` ≥ **2200**. 2. `transient_error` **≤ 1020** (было 1390: −351 премарк, −25 повтор, плюс новые отказы прогона). 3. `no_params` ≥ **2500** (было 385). 4. `ok` вырос **≥ 30** (было 1675). 5. Провал: `retried=0` или `premarked=0` → код не доехал; смотреть КОД в контейнере, не `SENTRY_RELEASE`. Refs #2674
bot-backend added 1 commit 2026-08-12 15:57:42 +00:00
fix(tradein/imv): «временная» ошибка снова временная — 1390 домов возвращаются в очередь
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI / backend-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 10s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (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 4m21s
2b7831d1d8
Очередь домовой IMV-оценки выбирает РОВНО ОДИН imv_status за прогон
(only_status, дефолт 'pending'). Повтор существовал только как значение этого
параметра — и за всю историю (41 прогон, 26.06–11.08) его не передал ни один
вызов. Дома, упавшие по инфраструктурной причине, которой больше нет (503/500
от браузерного сайдкара, #2698; за 7 суток до 12.08 в его access-логе 108 из
108 POST /fetch-json = 200), оставались в 'transient_error' навсегда: 1390 штук
на 12.08.2026, у всех есть и адрес, и параметры.

Что сделано:

1. Отдельная retry-очередь на половине пакета. Не OR к основной выборке: у
   pending last_imv_attempt_at всегда NULL, поэтому при общем
   ORDER BY ... NULLS FIRST повтор ждал бы опустошения pending (~5.7 тыс. домов
   ≈ год). Недобор retry-очереди возвращается pending — пакет не простаивает.
   Явный only_status отключает автоповтор: ручной запрос оператора обрабатывает
   ровно тот статус, который попросили, включая исчерпавшие лимит дома.

2. Выход из повтора — счётчик imv_transient_attempts (миграция 257), растёт
   только на transient_error, обнуляется успехом, порог 3. Число из замера:
   после починки сайдкара доля отказов на попытку 2/27 и 3/25 (прогоны
   3708/3467) ≈ 10%, то есть порог стоит максимум ~115 слотов ВСЕГО. Статус не
   переименовывается: «застряли окончательно» = transient_error AND
   imv_transient_attempts >= 3.

3. Половина пакета отдана повтору не за счёт pending: дома, по которым запрос к
   площадке невозможен, теперь помечаются no_params одним запросом до пакета
   (тот же вердикт и те же строки причин, что у поштучного пути). На проде это
   2276 домов из 6533 в очередях — 45 прогонов расписания (~136 суток) уходило
   на то, чтобы поштучно выяснить то, что видно в SQL. До правки до площадки
   доходили 17 домов из 50 (замер головы очереди 12.08), после — 50 из 50.

Пропускная способность (пакет 50 / 3 суток): pending 3218 + повтор 1039 после
премарка опустошаются за ~88 тактов (~264 суток) против 103 тактов (~309 суток)
на одном pending сейчас и «никогда» для 1390 застрявших.

Refs #2674
bot-backend merged commit c927b77777 into main 2026-08-12 16:06:25 +00:00
bot-backend deleted branch fix/house-imv-retry-stuck 2026-08-12 16:06:25 +00:00
Sign in to join this conversation.
No reviewers
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#2843
No description provided.