Annotation Type Retry


@Documented @Retention(RUNTIME) @Target({METHOD,TYPE}) public @interface Retry
Re-issues a request that fails with a transport error (a connection refused, a timeout) or with one of retryOnStatus(), up to times() attempts total, waiting delayMillis() between attempts and multiplying that wait by backoffMultiplier() after each one - unless the failed response itself carries a Retry-After header, which takes priority over the computed delay for that one wait (see delayMillis()), and jitterFactor() randomizes each computed delay to avoid many callers retrying in lockstep.

Works on both a synchronous return type and a CompletableFuture one - the async case schedules each retry on a background thread instead of blocking the caller. Every attempt, including ones that get retried, is still reported to any registered RequestInterceptor's afterResponse.

Retrying is only safe by default for a method whose HTTP verb is already idempotent (GET/PUT/DELETE) - retrying a POST/PATCH that actually succeeded server-side but whose response was lost in transit (a timeout or dropped connection after the server committed) risks double-executing it (a duplicate charge, a duplicate order). idempotent() closes that gap: it generates one Idempotency-Key header value and holds it identical across every attempt of a given call, so a server that honors idempotency keys (as Stripe, PayPal, Adyen, and Square all do) can recognize a retried attempt as the same logical request instead of a new one.

Also valid directly on a @RestClient interface, as a default for every method that doesn't declare its own @Retry - mirroring @BaseUrl's own interface-level-default/per-method-override shape, for an interface that wants one uniform retry policy instead of repeating the same annotation on every method. A method's own @Retry, if present, is used in full instead of the interface's - the two are never merged field-by-field.

  • Optional Element Summary

    Optional Elements
    Modifier and Type
    Optional Element
    Description
    double
    The factor the delay is multiplied by after each retry - 1.0 for a fixed delay, greater than 1.0 for exponential backoff.
    long
    How long to wait before the first retry, in milliseconds - overridden for that one wait whenever the response being retried carries a Retry-After header (as a delta-seconds value or an HTTP-date), since the server's own stated wait time is more authoritative than a value fixed at compile time.
    boolean
    If true, sends an Idempotency-Key header - one randomly generated value per call, held identical across every retry attempt of that call (never regenerated between attempts, never shared with any other call) - the standard way (Stripe, PayPal, Adyen, Square) to let a server tell a genuine retry of the same request apart from a new one.
    double
    Randomizes each computed delay (not one taken from a Retry-After header, which is already an explicit server instruction) by up to this fraction in either direction, so that many callers who all started retrying at the same moment - e.g. every replica of a horizontally-scaled service hitting the same downstream - don't retry in exact lockstep.
    int[]
    The HTTP status codes that count as a failure worth retrying.
    int
    How many attempts to make in total before giving up and returning (or throwing) the last outcome.
  • Element Details

    • times

      int times
      How many attempts to make in total before giving up and returning (or throwing) the last outcome. Must be at least 1.
      Returns:
      the maximum number of attempts
      Default:
      3
    • delayMillis

      long delayMillis
      How long to wait before the first retry, in milliseconds - overridden for that one wait whenever the response being retried carries a Retry-After header (as a delta-seconds value or an HTTP-date), since the server's own stated wait time is more authoritative than a value fixed at compile time. A transport failure (no response at all) has no header to read, so it always falls back to this value.
      Returns:
      the initial delay in milliseconds
      Default:
      200L
    • backoffMultiplier

      double backoffMultiplier
      The factor the delay is multiplied by after each retry - 1.0 for a fixed delay, greater than 1.0 for exponential backoff. Applied to the previous attempt's own delay, whether that delay came from this computation or from a Retry-After header.
      Returns:
      the backoff multiplier
      Default:
      2.0
    • jitterFactor

      double jitterFactor
      Randomizes each computed delay (not one taken from a Retry-After header, which is already an explicit server instruction) by up to this fraction in either direction, so that many callers who all started retrying at the same moment - e.g. every replica of a horizontally-scaled service hitting the same downstream - don't retry in exact lockstep. 0.0 (the default) applies no jitter, exactly reproducing the delay sequence delayMillis()/backoffMultiplier() alone would produce; 1.0 allows the delay to be scaled anywhere from zero to double. Must be between 0.0 and 1.0 inclusive.
      Returns:
      the jitter fraction
      Default:
      0.0
    • retryOnStatus

      int[] retryOnStatus
      The HTTP status codes that count as a failure worth retrying. A transport error (no response at all) is always retried regardless of this list.
      Returns:
      the retryable status codes
      Default:
      {429, 502, 503, 504}
    • idempotent

      boolean idempotent
      If true, sends an Idempotency-Key header - one randomly generated value per call, held identical across every retry attempt of that call (never regenerated between attempts, never shared with any other call) - the standard way (Stripe, PayPal, Adyen, Square) to let a server tell a genuine retry of the same request apart from a new one. Most meaningful on POST/PATCH, the two methods HTTP itself doesn't already guarantee are safe to retry; harmless (but redundant) on GET/PUT/DELETE.
      Returns:
      whether to send a stable Idempotency-Key
      Default:
      false