fix(ptica): два запроса connection-capacity перестают травить общую сессию (#2464) #2964

Merged
bot-backend merged 1 commit from fix/2464-capacity-savepoints into main 2026-08-20 09:53:35 +00:00
Collaborator

Пункт эпика #2464: full_report_pdf.py:217.

Куда указывал пункт и куда правлю

Пункт говорит: в except-блоке _get_connection_capacity нет db.rollback(). Но плоский rollback на общей сессии запрещён — он снёс бы работу вызывающего. Чинить надо там, где сбой происходит, как во всей волне #2464.

Нашёл источник: _query_power_points и _query_water_latest делают db.execute без SAVEPOINT, тогда как четыре соседних _query_* в том же файле его имеют.

_query_power_points          БЕЗ SAVEPOINT   ← правлю
_query_water_latest          БЕЗ SAVEPOINT   ← правлю
_query_gas_city_grs          защищён
_query_gas_outlet_counts     защищён
_query_gas_outlet_points     защищён
_query_heat_latest           защищён
_query_nearby_network_zones  защищён

Цепочка целиком

сбой в _query_power_points
  → исключение всплывает (в get_connection_capacity try/except нет)
  → его ГЛОТАЕТ _get_connection_capacity ради деградации §3
  → транзакция остаётся aborted
  → падает уже СЛЕДУЮЩИЙ запрос — рыночная цена в _generate_concept_result
  → концепция молча уходит в class_norm-фолбэк

Отказ проявляется не там, где случился, и выглядит как «нет рыночных данных».

Чего НЕ трогал

SET LOCAL statement_timeout в _query_nearby_network_zones при беглом поиске выглядит незащищённым (ближайший begin_nested дальше 16 строк), но лежит внутри savepoint'а — проверил по отступам.

Тест

На PostgresLikeSession (tests/support/pg_session.py) — двойнике с настоящей семантикой aborted-транзакции. На MagicMock такой тест был бы зелёным по построению: у него нет aborted-состояния.

Против origin/main:

следующий запрос после проглоченного сбоя  → AbortedTransactionError    ×2
savepoints_entered = 0 вместо ≥1           → падает                     ×2
двойник сам воспроизводит отравление       → контроль, зелёный с обеих сторон

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

Прогоны

tests/services/site_finder + tests/services/exporters   868 passed   rc=0
Пункт эпика #2464: `full_report_pdf.py:217`. ## Куда указывал пункт и куда правлю Пункт говорит: в `except`-блоке `_get_connection_capacity` нет `db.rollback()`. Но плоский `rollback` на **общей** сессии запрещён — он снёс бы работу вызывающего. Чинить надо там, где сбой происходит, как во всей волне #2464. Нашёл источник: `_query_power_points` и `_query_water_latest` делают `db.execute` **без SAVEPOINT**, тогда как четыре соседних `_query_*` в том же файле его имеют. ``` _query_power_points БЕЗ SAVEPOINT ← правлю _query_water_latest БЕЗ SAVEPOINT ← правлю _query_gas_city_grs защищён _query_gas_outlet_counts защищён _query_gas_outlet_points защищён _query_heat_latest защищён _query_nearby_network_zones защищён ``` ## Цепочка целиком ``` сбой в _query_power_points → исключение всплывает (в get_connection_capacity try/except нет) → его ГЛОТАЕТ _get_connection_capacity ради деградации §3 → транзакция остаётся aborted → падает уже СЛЕДУЮЩИЙ запрос — рыночная цена в _generate_concept_result → концепция молча уходит в class_norm-фолбэк ``` Отказ проявляется не там, где случился, и выглядит как «нет рыночных данных». ## Чего НЕ трогал `SET LOCAL statement_timeout` в `_query_nearby_network_zones` при беглом поиске выглядит незащищённым (ближайший `begin_nested` дальше 16 строк), но лежит **внутри** savepoint'а — проверил по отступам. ## Тест На `PostgresLikeSession` (`tests/support/pg_session.py`) — двойнике с настоящей семантикой aborted-транзакции. На `MagicMock` такой тест был бы зелёным по построению: у него нет aborted-состояния. Против `origin/main`: ``` следующий запрос после проглоченного сбоя → AbortedTransactionError ×2 savepoints_entered = 0 вместо ≥1 → падает ×2 двойник сам воспроизводит отравление → контроль, зелёный с обеих сторон ``` Последний контроль не для симметрии: без него зелёные тесты нельзя отличить от «двойник ничего не моделирует» — та же ловушка, что с `MagicMock`. ## Прогоны ``` tests/services/site_finder + tests/services/exporters 868 passed rc=0 ```
bot-backend added 1 commit 2026-08-20 09:30:56 +00:00
fix(ptica): два запроса connection-capacity перестают травить общую сессию (#2464)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI / changes (pull_request) Successful in 11s
CI Trade-In / backend-tests (pull_request) Has been skipped
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) Successful in 2m2s
CI / backend-tests (pull_request) Successful in 17m28s
2b646bc7e5
_query_power_points и _query_water_latest делали db.execute без SAVEPOINT, тогда
как четыре соседних _query_* в том же файле его имеют.

Пункт эпика указывал на другое место — отсутствие db.rollback() в except-блоке
_get_connection_capacity (full_report_pdf.py:217). Но плоский rollback на ОБЩЕЙ
сессии запрещён: он снёс бы работу вызывающего. Чинить надо там, где сбой
происходит, — как во всей волне #2464.

Цепочка целиком:

  сбой в _query_power_points
    → исключение всплывает (в get_connection_capacity try/except нет)
    → его глотает _get_connection_capacity ради деградации §3
    → транзакция остаётся aborted
    → падает уже СЛЕДУЮЩИЙ запрос — рыночная цена в _generate_concept_result
    → концепция молча уходит в class_norm-фолбэк

То есть отказ проявляется не там, где случился, и выглядит как «нет рыночных
данных». Ровно этот класс и описывает эпик.

Третье место (SET LOCAL statement_timeout в _query_nearby_network_zones) при
беглом поиске выглядит незащищённым, но лежит ВНУТРИ savepoint'а — проверено по
отступам, трогать не нужно.

Тест на PostgresLikeSession (tests/support/pg_session.py) — двойнике с настоящей
семантикой aborted-транзакции. На MagicMock он был бы зелёным по построению.
Против origin/main:

  следующий запрос после проглоченного сбоя  → AbortedTransactionError, ×2
  savepoints_entered = 0 вместо >=1          → падает, ×2
  двойник сам воспроизводит отравление       — контроль, зелёный с обеих сторон

Последний контроль не для симметрии: без него зелёные тесты нельзя отличить от
«двойник ничего не моделирует».

Прогоны: tests/services/site_finder + tests/services/exporters — 868 passed rc=0.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
bot-backend merged commit 7abc0ed431 into main 2026-08-20 09:53:35 +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#2964
No description provided.