retryInterceptor function

Interceptor retryInterceptor([
  1. RetryConfig config = const RetryConfig()
])

Builds a Connect Interceptor that retries failed unary calls per RetryConfig. Without arguments, uses RFC 0006 defaults.

Composition order, outermost to innermost (per RFC 0002): OTel -> Breaker -> Idempotency -> Retry -> Auth.

Implementation

Interceptor retryInterceptor([RetryConfig config = const RetryConfig()]) {
  final jitter = config.jitterSource ?? defaultJitterSource;
  final sleep = config.sleep ?? _realSleep;
  final maxAttempts = math.max(2, config.maxAttempts);
  final initial = config.initial.inMicroseconds <= 0
      ? const Duration(milliseconds: 100)
      : config.initial;
  final maxDelay = config.max.inMicroseconds < initial.inMicroseconds
      ? initial
      : config.max;
  final multiplier = math.max(1.0, config.multiplier);
  final decorrelation = config.decorrelationFactor < 1.0
      ? 3.0
      : config.decorrelationFactor;

  return <I extends Object, O extends Object>(AnyFn<I, O> next) {
    return (Request<I, O> req) async {
      // Streaming is never retried: replaying a stream of inputs is not safe.
      if (req.spec.streamType != StreamType.unary) {
        return next(req);
      }
      // Idempotency gate: methods the schema does not annotate are skipped
      // unless the caller explicitly opts in.
      if (!config.allowNonIdempotent && req.spec.idempotency == null) {
        return next(req);
      }

      var ceiling = initial;
      var prev = initial;
      var attempt = 0;
      while (true) {
        attempt++;
        try {
          return await next(req);
        } on ConnectException catch (error) {
          final isLast = attempt >= maxAttempts;
          if (isLast || !_shouldRetry(error, config)) {
            rethrow;
          }
          final delay = _nextDelay(
            error: error,
            ceiling: ceiling,
            prev: prev,
            config: config,
            maxDelay: maxDelay,
            initial: initial,
            decorrelation: decorrelation,
            jitter: jitter,
          );
          await sleep(delay);
          prev = delay;
          ceiling = _grow(ceiling, multiplier, maxDelay);
        }
      }
    };
  };
}