fix(site_finder): SAVEPOINT-isolate DB errors across cluster A (#2464) #2465

Merged
bot-backend merged 1 commit from worktree-agent-a22f8bf2fcf6325c7 into main 2026-07-07 12:50:12 +00:00
Collaborator

Что

Первая волна фиксов эпика #2464 (exhaustive backend audit) — подкластер A: session-poisoning в site_finder (7 сайтов).

Баг-класс

Функции, работающие в середине /analyze на общей request-scoped SQLAlchemy-сессии (Depends(get_db)), ловили ошибку db.execute (OperationalError/ProgrammingError/…), логировали и возвращали фолбэк — без rollback. Postgres оставляет транзакцию в состоянии aborted, и любой следующий db.execute в том же запросе падает current transaction is aborted. Это молча ломало downstream-шаги анализа и persist_analysis_run (строка analysis_runs не сохранялась → enqueue прогноза не срабатывал), хотя клиент получал 200.

Фикс

Оборачиваем db.execute в SAVEPOINT — with db.begin_nested(): (устоявшийся паттерн репозитория, референс — connection_capacity_lookup._query_nearby_network_zones и предупреждение в velocity.py:170). Плоский db.rollback() на shared-сессии запрещён (orphan'ит outer SessionTransaction).

Файл Сайт Паттерн Сессия
developer_attribution.py get_developer_attribution (CRITICAL) begin_nested shared
connection_capacity_lookup.py gas/heat-хелперы (×4) begin_nested shared
competitors.py avg_price / sold-count / objective-fallback (×3) begin_nested shared
saturation.py compute_district_saturation begin_nested (внутренний — внешний SAVEPOINT caller'а не помогал: исключение глоталось внутри) shared
supply_layers.py _safe_rows (L1/L2/L3) begin_nested shared+owned
zone_regulation.py backfill_ekb_zone_regulations db.rollback() owned (Celery SessionLocal())
pat_lookup.py parcel_pat_subzones begin_nested shared

Фолбэк-значения и scope except не менялись — добавлен только SAVEPOINT/rollback.

Тесты

Новые регресс-тесты на usability сессии после сбоя (не просто graceful-degrade): мок db.execute падает один раз → функция возвращает фолбэк → следующий db.execute на той же сессии успешен. 152/152 таргетных теста зелёные, ruff check/ruff format чисто.

Ревью

code-reviewer: APPROVE, ноль блокеров — перепроверены выбор паттерна per-site, shape SAVEPOINT (rollback до глотания исключения), владелец сессии zone_regulation, отсутствие :x::type, отсутствие tradein-файлов.

Refs #2464 (cluster A).

## Что Первая волна фиксов эпика **#2464** (exhaustive backend audit) — подкластер **A: session-poisoning** в `site_finder` (7 сайтов). ### Баг-класс Функции, работающие в середине `/analyze` на **общей** request-scoped SQLAlchemy-сессии (`Depends(get_db)`), ловили ошибку `db.execute` (OperationalError/ProgrammingError/…), логировали и возвращали фолбэк — **без rollback**. Postgres оставляет транзакцию в состоянии `aborted`, и любой следующий `db.execute` в том же запросе падает `current transaction is aborted`. Это молча ломало downstream-шаги анализа **и** `persist_analysis_run` (строка `analysis_runs` не сохранялась → enqueue прогноза не срабатывал), хотя клиент получал 200. ### Фикс Оборачиваем `db.execute` в SAVEPOINT — `with db.begin_nested():` (устоявшийся паттерн репозитория, референс — `connection_capacity_lookup._query_nearby_network_zones` и предупреждение в `velocity.py:170`). Плоский `db.rollback()` на shared-сессии запрещён (orphan'ит outer SessionTransaction). | Файл | Сайт | Паттерн | Сессия | |---|---|---|---| | `developer_attribution.py` | `get_developer_attribution` (**CRITICAL**) | `begin_nested` | shared | | `connection_capacity_lookup.py` | gas/heat-хелперы (×4) | `begin_nested` | shared | | `competitors.py` | avg_price / sold-count / objective-fallback (×3) | `begin_nested` | shared | | `saturation.py` | `compute_district_saturation` | `begin_nested` (внутренний — внешний SAVEPOINT caller'а не помогал: исключение глоталось внутри) | shared | | `supply_layers.py` | `_safe_rows` (L1/L2/L3) | `begin_nested` | shared+owned | | `zone_regulation.py` | `backfill_ekb_zone_regulations` | `db.rollback()` | **owned** (Celery `SessionLocal()`) | | `pat_lookup.py` | `parcel_pat_subzones` | `begin_nested` | shared | Фолбэк-значения и scope `except` не менялись — добавлен только SAVEPOINT/rollback. ## Тесты Новые регресс-тесты на **usability сессии после сбоя** (не просто graceful-degrade): мок `db.execute` падает один раз → функция возвращает фолбэк → следующий `db.execute` на той же сессии успешен. 152/152 таргетных теста зелёные, `ruff check`/`ruff format` чисто. ## Ревью code-reviewer: ✅ APPROVE, ноль блокеров — перепроверены выбор паттерна per-site, shape SAVEPOINT (rollback до глотания исключения), владелец сессии `zone_regulation`, отсутствие `:x::type`, отсутствие tradein-файлов. Refs #2464 (cluster A).
bot-backend added 1 commit 2026-07-07 12:33:16 +00:00
fix(site_finder): SAVEPOINT-isolate DB errors across cluster A (#2464)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 2m5s
CI / backend-tests (pull_request) Successful in 15m6s
CI / changes (pull_request) Successful in 9s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
c52c2c4063
developer_attribution, connection_capacity_lookup (gas/heat helpers),
competitors, saturation, supply_layers, and pat_lookup swallowed DB errors
without begin_nested()/rollback(), leaving the shared analyze-request
Session aborted for every subsequent db.execute (silently breaking
persist_analysis_run and the forecast enqueue). zone_regulation's
backfill_ekb_zone_regulations gets a plain rollback() since it only ever
runs on its own owned Celery-task session.

Refs #2464 (cluster A session-poisoning).
bot-backend merged commit 39dd63333e into main 2026-07-07 12:50:12 +00:00
bot-backend deleted branch worktree-agent-a22f8bf2fcf6325c7 2026-07-07 12:50:13 +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#2465
No description provided.