Какие есть стратегии ретраев
Что проверяют
Понимаете ли вы, что повтор может как спасти запрос, так и добить перегруженный сервис. Следом почти всегда спрашивают, какие ошибки вообще можно повторять и что будет с неидемпотентной операцией.
Как отвечать
Стратегия складывается из трёх решений: что повторять, сколько раз и с какой паузой.
- фиксированная задержка: простой вариант для фоновых задач;
- экспоненциальная задержка: пауза растёт как
base * 2^nс верхним пределом; - джиттер: к паузе добавляется случайная часть. Без него тысяча клиентов, упавших одновременно, так же одновременно и вернётся;
- лимиты: максимум попыток, общий дедлайн на операцию и бюджет ретраев, то есть доля повторов от общего потока, выше которой повторять перестают.
Повторять имеет смысл таймауты, обрывы соединения, 503 и 429 с учётом заголовка Retry-After. Ошибки 400, 401, 404 повтор не исправит. Неидемпотентную операцию вроде оплаты повторяют только с ключом идемпотентности, который проверяет сервер.
В Java это обычно Resilience4j: RetryConfig с maxAttempts (по умолчанию 3, включая первый вызов) и IntervalFunction.ofExponentialRandomBackoff для экспоненты с джиттером. В Spring есть Spring Retry с @Retryable и @Backoff(delay = 200, multiplier = 2), а в Spring Framework 7 @Retryable появился прямо в ядре. Для Kafka есть DefaultErrorHandler с ExponentialBackOff и неблокирующие повторы через @RetryableTopic с DLT.
Circuit breaker стратегией ретраев не является, это соседний механизм: после серии отказов он перестаёт звать сервис совсем. В Resilience4j Retry по умолчанию оборачивает CircuitBreaker, так что каждая попытка попадает в статистику брейкера, а CallNotPermittedException стоит исключить из повторяемых.
Где спотыкаются
Ставят ретраи на каждом уровне. Три слоя по три попытки дают 27 запросов к упавшей базе на один клик пользователя.
Повторяют всё подряд, включая ошибки валидации, и не думают об идемпотентности. Итог: двойное списание денег.
Забывают, что сумма пауз и таймаутов должна уложиться в таймаут вызывающей стороны. Иначе клиент давно ушёл, а сервис всё ещё старательно повторяет.