Наблюдение
Реактивное состояние флуда персистентно: accounts.flood_wait_until
(src/database/schema.py:9), пишется в pool_flood.py:241-246, читается
на каждом выборе аккаунта (account_lease_pool.py:188-191). Переживает рестарт.
Проактивное состояние — только в памяти:
| Механизм |
Хранилище |
Файл |
TelegramRateLimitGate |
self._limiters — обычный dict |
rate_limit_gate.py:141 |
FloodCircuitBreaker |
self._breakers — обычный dict |
flood_breaker.py:124 |
ResolveRateLimiter |
deque в памяти |
rate_limiter.py:66 |
Рестарт воркера обнуляет все три. Формально это означает, что после перезапуска
процесс стартует с чистым окном и может немедленно выдать полный залп, а автостоп
(3 флуда → пауза 300 с) забывает уже накопленные срабатывания.
Грепом подтверждено: вопрос перезапуска в этих файлах вообще не обсуждается —
ни слова о restart/персистентности в комментариях. То есть это не осознанный
компромисс с обоснованием, а слепое пятно.
⚠️ Сначала замер, только потом фикс
Это issue НЕ на реализацию персистентности. Сначала надо доказать по данным,
что проблема реальна, иначе это фикс из головы — ровно то, чего проект избегает
(принцип: калибровать замером, а не догадкой).
Что померить по data/app.log и логам рестартов:
- Как часто воркер вообще перезапускается в реальной эксплуатации.
Если раз в неделю — проблема умозрительная, и issue закрывается как
«не подтвердилось».
- Случались ли флуды в первые секунды после старта — то есть реализовался
ли сценарий «забыли окно → сразу залп». Искать корреляцию отметок старта
процесса и всплесков Flood wait.
- Терялось ли состояние автостопа в момент, когда оно было нужно — то есть
был ли рестарт между накопленными флудами на одной паре (операция, телефон).
Это наиболее опасный сценарий: автостоп существует именно против серии,
приведшей к бану на 14.8 ч (flood_breaker.py:10-14 — 85 флудов за сутки).
Если замер подтвердит — тогда решать как чинить
Варианты (не выбирать заранее):
- Персистить только состояние автостопа (самое ценное, самое редко меняющееся),
оставив окна лимитера в памяти.
- Персистить всё через существующий механизм настроек — прецедент есть:
resolve-backoff уже персистится в settings JSON (resolve_guard.py:255-300),
включая миграцию старого формата.
- Ничего не персистить, но при старте применять «прогревочную» паузу.
⚠️ PyrateLimiter с его SQLiteBucket не подходит: синхронный, ведёт свой
sqlite3.Connection мимо Database._write_lock (правило проекта из #569 —
все записи только через db.transaction() / db.execute_write()).
Acceptance Criteria
Файлы
src/telegram/rate_limit_gate.py:141 — _limiters
src/telegram/flood_breaker.py:124 — _breakers, :220 — reset
src/telegram/rate_limiter.py:66 — ResolveRateLimiter
src/telegram/resolve_guard.py:255-300 — прецедент персистентности через settings
data/app.log, логи рестартов воркера
Контекст
Часть Phase 2 эпика #1331. Обнаружено при редизайне #1416.
Отдельно отмечено исследователем: чинить только после замера, иначе фикс из головы.
Наблюдение
Реактивное состояние флуда персистентно:
accounts.flood_wait_until(
src/database/schema.py:9), пишется вpool_flood.py:241-246, читаетсяна каждом выборе аккаунта (
account_lease_pool.py:188-191). Переживает рестарт.Проактивное состояние — только в памяти:
TelegramRateLimitGateself._limiters— обычныйdictrate_limit_gate.py:141FloodCircuitBreakerself._breakers— обычныйdictflood_breaker.py:124ResolveRateLimiterdequeв памятиrate_limiter.py:66Рестарт воркера обнуляет все три. Формально это означает, что после перезапуска
процесс стартует с чистым окном и может немедленно выдать полный залп, а автостоп
(3 флуда → пауза 300 с) забывает уже накопленные срабатывания.
Грепом подтверждено: вопрос перезапуска в этих файлах вообще не обсуждается —
ни слова о restart/персистентности в комментариях. То есть это не осознанный
компромисс с обоснованием, а слепое пятно.
Это issue НЕ на реализацию персистентности. Сначала надо доказать по данным,
что проблема реальна, иначе это фикс из головы — ровно то, чего проект избегает
(принцип: калибровать замером, а не догадкой).
Что померить по
data/app.logи логам рестартов:Если раз в неделю — проблема умозрительная, и issue закрывается как
«не подтвердилось».
ли сценарий «забыли окно → сразу залп». Искать корреляцию отметок старта
процесса и всплесков
Flood wait.был ли рестарт между накопленными флудами на одной паре (операция, телефон).
Это наиболее опасный сценарий: автостоп существует именно против серии,
приведшей к бану на 14.8 ч (
flood_breaker.py:10-14— 85 флудов за сутки).Если замер подтвердит — тогда решать как чинить
Варианты (не выбирать заранее):
оставив окна лимитера в памяти.
resolve-backoff уже персистится в settings JSON (
resolve_guard.py:255-300),включая миграцию старого формата.
PyrateLimiterс егоSQLiteBucketне подходит: синхронный, ведёт свойsqlite3.ConnectionмимоDatabase._write_lock(правило проекта из #569 —все записи только через
db.transaction()/db.execute_write()).Acceptance Criteria
«старт → всплеск флудов», случаи потери состояния автостопа.
полноценный результат, issue закрывается без кода.
(правило epic: замена самописного кода на battle-tested библиотеки (реестр находок + решений) #782: замена/подключение обсуждается заранее).
Файлы
src/telegram/rate_limit_gate.py:141—_limiterssrc/telegram/flood_breaker.py:124—_breakers,:220—resetsrc/telegram/rate_limiter.py:66—ResolveRateLimitersrc/telegram/resolve_guard.py:255-300— прецедент персистентности через settingsdata/app.log, логи рестартов воркераКонтекст
Часть Phase 2 эпика #1331. Обнаружено при редизайне #1416.
Отдельно отмечено исследователем: чинить только после замера, иначе фикс из головы.