Кейс: retry без jitter синхронизировал нагрузку и уронил то, что уже восстанавливалось
Downstream-сервис начал отдавать 503 из-за короткого всплеска нагрузки. Десятки инстансов клиента получили ошибку почти одновременно, потому что все ходили в downstream в рамках одного batch-задания по расписанию.
Retry был настроен с фиксированной задержкой 500 мс, без jitter. Все инстансы поймали ошибку в одном временном окне и повторили запрос ровно через 500 мс — тоже все одновременно. Downstream, который к этому моменту начал восстанавливаться, получил второй синхронный всплеск и лёг заново.
Цикл повторился ещё дважды на следующих retry, пока не сработал circuit breaker и часть трафика не отвалилась по таймауту. Исправили добавлением decorrelated jitter — задержка перед retry стала случайной в растущем диапазоне, а не фиксированной.
long delay = ThreadLocalRandom .current() .nextLong(base, base * 3);
Спросят следом: почему exponential backoff без jitter не решает проблему сам. Потому что все клиенты стартуют с одинаковой base delay и одинаково её увеличивают — синхронность сохраняется на каждом шаге, просто с большим периодом.
Backoff без jitter синхронизирует ошибку так же надёжно, как синхронизирует обычную нагрузку.
Тренажёр: 600 вопросов, мок с таймером, план повторов
senior·base — что спрашивают на самом деле
В этом посте были ссылки, но мы их удалили по правилам Сетки