Троекратный всплеск соединений с БД из-за цикла на клиенте

Симптом

Утром количество соединений одного из сервисов с базой данных внезапно выросло примерно втрое. При этом в логах приложения не было ошибок, а пользователи не видели явного падения сервиса.

Контекст

Наблюдение показало тысячи одинаковых успешных запросов на загрузку истории одного чата. Все они возвращали 200, поэтому обычный мониторинг ошибок не срабатывал. Примерно через четверть часа пул соединений освободился и нагрузка вернулась к норме.

Незадолго до этого пользователь сообщал, что ответы нейросети не появлялись в одном браузере, но после смены браузера проблема исчезла. В момент всплеска повторные запросы исходили из того же сценария.

Корневая причина

Наблюдаемая причина нагрузки - клиентский цикл, многократно запрашивавший одну и ту же историю чата. Наиболее вероятный триггер - сбой или неполная финализация SSE-потока: клиент, не получив ожидаемое завершение ответа, продолжал попытки синхронизации.

Точный набор факторов, который запустил цикл в конкретном браузере, не был доказан. Это могли быть особенности обработки сети, сна устройства или восстановления соединения. Но серверная часть не имела ограничения, которое остановило бы такой паттерн запросов.

Ошибочное предположение

Мы считали, что если сценарий стабилен в одном браузере, он будет стабилен и в остальных. Кроме того, успешный статус 200 у каждого отдельного запроса создавал ложное ощущение, что система работает нормально.

Обнаружение

Ранним сигналом должен быть не только процент ошибок, но и интенсивность одинаковых запросов: один пользователь или клиент многократно запрашивает один и тот же ресурс за короткий интервал.

Для этого нужны метрики запросов по ресурсу и клиенту, а также алерт на всплеск повторяющихся запросов при отсутствии соответствующего пользовательского действия.

Исправление

Исправление сделали на двух границах:

Rate limit защищает базу даже от ошибочного клиента, а guard устраняет сам источник лишних запросов. Одного из этих уровней недостаточно: клиент может сломаться снова, а сервер не должен позволять такому сбою превратиться в неограниченную нагрузку.

Проверка

Минимальная регрессионная проверка должна имитировать незавершённый SSE-ответ и убедиться, что клиент не запускает бесконечный цикл загрузки истории. Отдельно нужно проверить server-side rate limit: после заданного числа одинаковых запросов сервер должен ограничить дальнейшие обращения, не занимая новый ресурс базы для каждого повтора.

В исходной заметке автоматический тест для этого сценария не зафиксирован.

Общий вывод

Отсутствие ошибок не означает отсутствие аварийной нагрузки. Надёжность нужно проверять и по поведению запросов: повторяемость, частота и длительность могут быть опаснее единичного ответа с ошибкой. Для клиентских streaming-сценариев защита должна существовать и на клиенте, и на сервере.