Какая причина делает экспоненциальные повторы без случайной задержки опасными для восстановившейся зависимости?
Без случайной задержки клиенты, обнаружившие сбой примерно одновременно, повторяют запросы по одному и тому же расписанию. После восстановления зависимости эти повторы синхронно создают всплеск нагрузки — retry storm, который может снова перегрузить сервис и вызвать новый отказ.
Экспоненциальная задержка уменьшает частоту повторов, но без jitter не устраняет их синхронизацию. Поэтому обычно применяют ограниченный экспоненциальный backoff со случайным разбросом задержки.
Повторные попытки появились как практический способ переживать временные сетевые сбои, перегрузки и кратковременную недоступность сервисов. Однако независимые клиенты часто реагируют на один отказ почти одновременно, особенно если используют одинаковые тайм-ауты и алгоритмы повторов.
Так возникла проблема thundering herd: большое число участников одновременно обращается к освободившемуся ресурсу. Идея случайного разброса времени повтора применяется для рассинхронизации таких участников; аналогичный принцип используется в алгоритмах управления конкуренцией и выбора лидера.
Пусть зависимость временно недоступна, а тысячи клиентов получают тайм-аут почти в один момент. Если каждый клиент повторяет запрос через 1, 2, 4 и 8 секунд, моменты повторов совпадают или оказываются очень близкими.
Когда зависимость восстанавливается, она получает не обычный поток запросов, а накопившийся синхронный всплеск. Часть запросов снова завершается тайм-аутом, клиенты планируют следующий массовый повтор, а задержки и загрузка начинают расти по замкнутому циклу.
Неверно считать, что одного увеличения интервалов достаточно. Backoff снижает среднюю интенсивность повторов, но при одинаковом расписании сохраняет пики нагрузки.
Используют три элемента:
Jitter распределяет запросы во времени. Даже если клиенты начали повтор одновременно, их следующие попытки с высокой вероятностью разойдутся, поэтому восстановившийся сервис получает более плавный поток.
На практике также учитывают тип ошибки. Повтор обычно уместен для временных сетевых сбоев и перегрузки, но не для постоянной ошибки валидации или авторизации. Ответ Retry-After, если он поддерживается протоколом, позволяет зависимости явно сообщить рекомендуемую паузу.
Повторы должны быть ограничены бюджетом времени и согласованы с идемпотентностью операции. Для операций, изменяющих состояние, один только backoff не защищает от повторного выполнения: нужны идемпотентный ключ, дедупликация или другая гарантия на уровне операции.
Слишком большой jitter может ухудшить задержку отдельных запросов, а слишком маленький — недостаточно рассинхронизировать клиентов. Агрессивные повторы уменьшают время восстановления для единичного сбоя, но увеличивают риск перегрузки; консервативные повторы безопаснее для зависимости, но повышают пользовательскую задержку.
Платёжный сервис временно перестал отвечать. Все экземпляры интернет-магазина имеют одинаковый тайм-аут и после него повторяют запросы через 1, 2 и 4 секунды. После восстановления платёжного сервиса почти все экземпляры отправляют повтор одновременно, из-за чего он снова начинает отвечать с тайм-аутами.
Рассматривались три варианта. Полный отказ от повторов снижает нагрузку, но превращает кратковременный сбой в массовые ошибки для пользователей. Фиксированная задержка проще, но сохраняет синхронизацию. Экспоненциальный backoff без jitter уменьшает среднюю частоту запросов, однако оставляет пики.
Выбрали ограниченный экспоненциальный backoff с jitter, максимальным числом попыток и отдельным контролем идемпотентности платежной операции. Для постоянных ошибок повторы отключаются, а при перегрузке учитывается сигнал сервиса о рекомендуемой задержке.
В результате восстановившаяся зависимость получает распределённый во времени поток запросов, а система не превращает единичный отказ в каскадный. При этом платёж не выполняется повторно только из-за того, что клиент не получил ответ о результате первой попытки.
Экспоненциальный backoff изменяет интервалы, но одинаково настроенные клиенты сохраняют одинаковую фазу: они продолжают повторять почти одновременно. Jitter случайно смещает эту фазу и превращает периодические пики в более равномерный поток.
Нет. Повтор безопасен только при понимании семантики операции и возможного результата первой попытки. Для изменения состояния требуется идемпотентность или дедупликация; иначе клиент может получить тайм-аут, повторить запрос и выполнить действие дважды.
Circuit breaker временно прекращает обращения при устойчивых сбоях и тем самым защищает зависимость от постоянного потока запросов. Но после перехода в состояние half-open несколько клиентов всё равно могут одновременно начать пробные вызовы. Backoff и jitter управляют расписанием этих попыток, поэтому механизмы дополняют друг друга, а не заменяют.