(medium) scrape_pipeline.run_avito_city_sweep: IMV-фаза отравляет сессию → update_heartbeat без rollback переводит успешный sweep в 'failed' #1521

Closed
opened 2026-06-15 05:59:11 +00:00 by bot-backend · 1 comment
Collaborator

Severity: medium · Категория: data-integrity · Файл: tradein-mvp/backend/app/services/scrape_pipeline.py:593-602

scrape_pipeline.py:593-602 (avito city sweep, IMV-финализация). Если process_houses_imv_batch (house_imv_backfill.py:458) бросает исключение, оставив SQLAlchemy-сессию в aborted-state (например, упал необёрнутый eligibility SELECT house_imv_backfill.py:489-505, или _mark_status на уже отравленной сессии — 311-327), обработчик except (593-600) НЕ делает db.rollback(): логирует, errors_count++ и сразу зовёт update_heartbeat (600). update_heartbeat (scrape_runs.py:117-129) и mark_done (141-157) — единственные финализаторы БЕЗ defensive rollback (mark_failed 166-169 / mark_banned 198-201 его имеют). На aborted-сессии update_heartbeat падает с PendingRollbackError вне внутреннего try → внешний except (622) → mark_failed. Импакт: полностью успешный sweep (все anchor'ы отработали, lots закоммичены) ложно помечается 'failed' из-за краха НЕОБЯЗАТЕЛЬНОЙ IMV-фазы; docstring (595) обещает обратное. Чинить: добавить try: db.rollback() except: pass перед update_heartbeat в except (599-600) И/ИЛИ внести defensive rollback в начало update_heartbeat/mark_done (по образцу mark_failed/mark_banned scrape_runs.py:166-169).

Почему баг: mark_done (стр. 602) и update_heartbeat (стр. 600) — единственные финализаторы здесь, которые НЕ имеют defensive db.rollback() (в отличие от mark_failed/mark_banned, см. scrape_runs.py:166-169, 198-201). Документация функции прямо обещает 'sweep still done' при крахе IMV (стр. 595), но реализация этого не гарантирует, если IMV отравил сессию: первый же commit на aborted-сессии перебрасывает run в failed.

Как чинить: В except IMV-фазы сначала вызвать db.rollback() (best-effort try/except), затем update_heartbeat/mark_done. Либо обернуть финализацию в try и при ошибке всё равно звать mark_done после rollback.


deep-audit «армия ботов 2» · verify+harden, confidence 0.82 · unit U125

**Severity:** medium · **Категория:** data-integrity · **Файл:** `tradein-mvp/backend/app/services/scrape_pipeline.py:593-602` scrape_pipeline.py:593-602 (avito city sweep, IMV-финализация). Если process_houses_imv_batch (house_imv_backfill.py:458) бросает исключение, оставив SQLAlchemy-сессию в aborted-state (например, упал необёрнутый eligibility SELECT house_imv_backfill.py:489-505, или _mark_status на уже отравленной сессии — 311-327), обработчик except (593-600) НЕ делает db.rollback(): логирует, errors_count++ и сразу зовёт update_heartbeat (600). update_heartbeat (scrape_runs.py:117-129) и mark_done (141-157) — единственные финализаторы БЕЗ defensive rollback (mark_failed 166-169 / mark_banned 198-201 его имеют). На aborted-сессии update_heartbeat падает с PendingRollbackError вне внутреннего try → внешний except (622) → mark_failed. Импакт: полностью успешный sweep (все anchor'ы отработали, lots закоммичены) ложно помечается 'failed' из-за краха НЕОБЯЗАТЕЛЬНОЙ IMV-фазы; docstring (595) обещает обратное. Чинить: добавить try: db.rollback() except: pass перед update_heartbeat в except (599-600) И/ИЛИ внести defensive rollback в начало update_heartbeat/mark_done (по образцу mark_failed/mark_banned scrape_runs.py:166-169). **Почему баг:** mark_done (стр. 602) и update_heartbeat (стр. 600) — единственные финализаторы здесь, которые НЕ имеют defensive db.rollback() (в отличие от mark_failed/mark_banned, см. scrape_runs.py:166-169, 198-201). Документация функции прямо обещает 'sweep still done' при крахе IMV (стр. 595), но реализация этого не гарантирует, если IMV отравил сессию: первый же commit на aborted-сессии перебрасывает run в failed. **Как чинить:** В except IMV-фазы сначала вызвать db.rollback() (best-effort try/except), затем update_heartbeat/mark_done. Либо обернуть финализацию в try и при ошибке всё равно звать mark_done после rollback. --- <sub>deep-audit «армия ботов 2» · verify+harden, confidence 0.82 · unit U125</sub>
bot-backend added the
week ревью 1
label 2026-06-15 05:59:11 +00:00
bot-backend added the
scope/backend
label 2026-06-15 11:02:17 +00:00
Owner

🔍 week ревью 1 — верификация (ПОДТВЕРЖДЕНО)

Текущее состояние кода (git show forgejo/main): scrape_pipeline.py IMV-финализация — except Exception as exc: логирует "...IMV phase crashed — %r (sweep still done)", делает counters.errors_count += 1 и сразу scrape_runs.update_heartbeat(db, run_id, counters.to_dict()) — БЕЗ db.rollback(). Сразу после блока — scrape_runs.mark_done(...). scrape_runs.py:update_heartbeat:117-129 (UPDATE ... heartbeat_at=NOW() ...; db.commit()) и mark_done:141-158 НЕ имеют defensive rollback, в отличие от mark_failed:166-167 и mark_banned:198-199 (там db.rollback() есть).

Корень: Если IMV-фаза оставила сессию в aborted-state (необёрнутый eligibility SELECT / _mark_status на отравленной сессии в house_imv_backfill.py), первый же commit на aborted-сессии — внутри update_heartbeat/mark_done — бросает PendingRollbackError вне внутреннего try → внешний except → mark_failed. Полностью успешный sweep (lots закоммичены) ложно помечается failed; docstring (sweep still done) нарушен.

Что править:

  • В except IMV-фазы перед update_heartbeat/mark_done вызвать best-effort try: db.rollback() except: pass.
  • И/или внести defensive db.rollback() в начало update_heartbeat и mark_done по образцу mark_failed/mark_banned (scrape_runs.py:166-167, :198-199).

Что посмотреть:

  • Код: scrape_pipeline.py run_avito_city_sweep IMV-блок (except Exception as exc + update_heartbeat + mark_done); scrape_runs.py:update_heartbeat:117, mark_done:141, эталоны mark_failed:160/mark_banned:189; house_imv_backfill.py:process_houses_imv_batch (eligibility SELECT, _mark_status). Тест: IMV бросает на aborted-сессии → sweep всё равно done.
  • Vault: релевантных заметок не найдено.

Примечание: верификация против forgejo/main (рабочая ветка feat/cian-city-sweep-schedule трогает scrape_pipeline.py — строки могут сдвинуться).

**🔍 week ревью 1 — верификация (ПОДТВЕРЖДЕНО)** **Текущее состояние кода (git show forgejo/main):** `scrape_pipeline.py` IMV-финализация — `except Exception as exc:` логирует `"...IMV phase crashed — %r (sweep still done)"`, делает `counters.errors_count += 1` и сразу `scrape_runs.update_heartbeat(db, run_id, counters.to_dict())` — БЕЗ `db.rollback()`. Сразу после блока — `scrape_runs.mark_done(...)`. `scrape_runs.py:update_heartbeat:117-129` (`UPDATE ... heartbeat_at=NOW() ...; db.commit()`) и `mark_done:141-158` НЕ имеют defensive rollback, в отличие от `mark_failed:166-167` и `mark_banned:198-199` (там `db.rollback()` есть). **Корень:** Если IMV-фаза оставила сессию в aborted-state (необёрнутый eligibility SELECT / `_mark_status` на отравленной сессии в `house_imv_backfill.py`), первый же commit на aborted-сессии — внутри `update_heartbeat`/`mark_done` — бросает `PendingRollbackError` вне внутреннего try → внешний except → `mark_failed`. Полностью успешный sweep (lots закоммичены) ложно помечается `failed`; docstring `(sweep still done)` нарушен. **Что править:** - В except IMV-фазы перед `update_heartbeat`/`mark_done` вызвать best-effort `try: db.rollback() except: pass`. - И/или внести defensive `db.rollback()` в начало `update_heartbeat` и `mark_done` по образцу `mark_failed`/`mark_banned` (`scrape_runs.py:166-167`, `:198-199`). **Что посмотреть:** - Код: `scrape_pipeline.py` `run_avito_city_sweep` IMV-блок (`except Exception as exc` + `update_heartbeat` + `mark_done`); `scrape_runs.py:update_heartbeat:117`, `mark_done:141`, эталоны `mark_failed:160`/`mark_banned:189`; `house_imv_backfill.py:process_houses_imv_batch` (eligibility SELECT, `_mark_status`). Тест: IMV бросает на aborted-сессии → sweep всё равно `done`. - Vault: релевантных заметок не найдено. Примечание: верификация против `forgejo/main` (рабочая ветка `feat/cian-city-sweep-schedule` трогает scrape_pipeline.py — строки могут сдвинуться).
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
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#1521
No description provided.