fix(tradein/support): один повтор терял каждое одиннадцатое сообщение в поддержку #3309
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
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: lekss361/gendesign#3309
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "fix/tgsupport-retry-budget"
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?
Что не так
Ручка веб-поддержки ходила в Telegram с
max_retries=1, то есть двумя попытками. Канал доapi.telegram.orgс прод-хоста рвётся постоянно, и при измеренной доле отказов до пользователя доходило порядка 9% отказов — каждое одиннадцатое сообщение возвращало 502 «сервис недоступен».Замер, на котором это стоит
Всё измерено 01.09.2026 из контейнера
tradein-tgbot, на живом проде.Канал рвётся, и это не наш клиент. Пять проб подряд, доля отказов на попытку: 3/8, 15/40, 5/20, 3/20, 1/25 — то есть 15-38% всплесками. В логе long-polling'а за сутки 353
ConnectTimeout. Транспорт ни при чём: в чередующемся замереhttpx(чем ходит бот) дал 25% отказов, сыройurllib— 35%.Прокси не решение — проверено, а не предположено. Через
SCRAPER_PROXY_URLкapi.telegram.org: 0 из 20. Узел скрейпинга туда просто не пускает, так что напрашивавшийся ход отброшен по замеру.Два числа задают всю конструкцию правки:
timeout=10.0), быстрых отказов — нольОтсюда следует, что десятисекундный таймаут не покупал ничего, кроме цены за неудачу. И что экспоненциальная пауза 2→4→8с здесь бессмысленна: отказ — это неустановленное соединение, а не троттлинг, удалённой стороне нечего «остывать»; пауза лишь добавляла 14 секунд к ожиданию пользователя.
Правка
Бюджет интерактивной отправки: 3 повтора, таймаут 5с, потолок паузы 1с.
В
TelegramClientдобавлен необязательныйmax_backoff.Что здесь легко сломать
Воркерная политика ретраев обязана остаться прежней. Без явного потолка откат прежний экспоненциальный до 30с, а
retry_afterиз тела 429 уважается целиком — иначе мы долбимся в лимит и Telegram затягивает его жёстче.Эту границу держит отдельный тест, и он не декоративный: первая версия правки её сломала. Потолок применялся к
retry_afterбезусловно, и воркер начинал спать 30с там, где Telegram просил 60. Тест поймал это до коммита. Теперь потолок наretry_afterприменяется только когда его передали явно — интерактивному пути ждать 30-60с нельзя ни при каких обстоятельствах, за ним стоит открытый HTTP-запрос от браузера.Тесты
8 новых (
tests/test_tgsupport_retry_budget.py): потолок на network/429/5xx, неизменность воркерного пути в обеих ветках, арифметика «max_retries=N→ N+1 попыток», границы бюджета ручки.Локально: 43 passed по
test_tgsupport_retry_budget.py+test_support.py, 105 passed по всему срезу-k "tgbot or telegram or support or bridge", ruff чист.Границы
Не трогает: приём (long-polling), маршрутизацию ответов операторов, схему, анонимную ветку по существу (она получает тот же бюджет, что и авторизованная). Сам разрыв канала до Telegram эта правка не чинит — она делает его переживаемым. Настоящее решение — отдельный egress до Telegram, но какой именно, надо подбирать замером; в этот PR не входит.