Ответ оператора не исчезает при сбое БД, и реплай на собственный ответ снова маршрутизируется #3479
No reviewers
Labels
No labels
Fable 5 ревью
GG-форсайт
admin
analytics
auth
automation
bug
business
chore
ci
compliance
data
data-moat
docs
duplicate
dx
enhancement
feedback/max
generative
needs-discussion
needs-human
observability
pause-bots
performance
priority/p0
priority/p1
priority/p2
priority/p3
scope/backend
scope/db
scope/devops
scope/frontend
scope/qa
scrapers
security
site-finder
stage/1
stage/2
status/blocked
status/done
status/needs-analysis
status/needs-fix
status/qa
status/ready
status/review
status/wip
tech-debt
tradein
ux
week ревью 1
wontfix
ИРД
вторичка
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: lekss361/gendesign#3479
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "fix/3471-bridge-db-failure-reply-loss"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Пункт P0 из #3471. Дефект, который PR #3456—#3459 не закрыли: они чинили HTTP-клиент, транспорт и ручки API, а эта ветка живёт в мосте.
Что было
В
process_updateветкаexcept SQLAlchemyErrorделалаrollback(), после чего безусловно выполнялисьsave_offsetиcommit. Для веб-треда записьrecord_web_out_message— это и есть доставка клиенту: веб-чат рисует переписку из базы. Откат записи вместе со сдвинутым offset означал, что Telegram этот апдейт больше не отдаст, ответ оператора не попадёт никуда, оператор будет уверен, что ответил, а клиент останется ждать.Случай уже наблюдался на проде 31.08.2026. Обращение оказалось тестовым, так что настоящий клиент не пострадал, — но только по везению.
Что сделано
Ответ не теряется молча. Веб-ветка в
_handle_group_replyобёрнута вtry/except SQLAlchemyError. На сбое: откат, затем реплай оператору в топик с прямым текстом «ответ НЕ доставлен, отправьте ещё раз». Offset после этого двигается осознанно: апдейт уже частично применён на стороне Telegram, переигрывать его нельзя. Образец взят из соседней обработки 403 «бот заблокирован».Уведомление не может уронить поток.
_notify_topicтеперь возвращает признак успеха, её собственный перехват по-прежнему не выпускает исключение наружу. Если и уведомление не ушло, остаётсяlogger.errorс идентификаторами треда и сообщения — по ним человек найдёт ответ в топике. Текст переписки в лог не пишется, это персональные данные.Реплай на свой же ответ снова работает. У строк с
direction='out'полеtopic_message_idвсегда было пустым, поэтому оператор, отвечавший реплаем на собственное предыдущее сообщение, говорил в пустоту. Теперь идентификатор сообщения оператора сохраняется и для веб-, и для TG-пути, а поиск по зеркалу больше не фильтрует по направлению.Миграция не потребовалась: колонка и частичный уникальный индекс уже существуют (186 и 187). Коллизий нет, Telegram выдаёт разным сообщениям в одном чате разные идентификаторы, и индекс это же гарантирует на уровне базы.
Тесты
pytest tests/services/tgbot/test_bridge.py -q— 41 passed. Новые случаи: сбой базы на веб-ответе с успешным уведомлением; сбой базы плюс провал уведомления; реплай на свой предыдущий веб-ответ; то же для TG-пути.Хвост
COMMENT ON COLUMNвdata/sql/187_web_support_chat.sqlвсё ещё говорит «только для direction='in'». На поведение не влияет, DDL не нужен, поправлю отдельным PR без кода.