Кейс: 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 — что спрашивают на самом деле


В этом посте были ссылки, но мы их удалили по правилам Сетки