Skip to content

Add jitter, Retry-After respect, and safety cap to StatusCodeAndExceptionRetry, Fixes AB#3536278#3009

Draft
Copilot wants to merge 2 commits intodevfrom
copilot/add-jitter-retry-after-cap
Draft

Add jitter, Retry-After respect, and safety cap to StatusCodeAndExceptionRetry, Fixes AB#3536278#3009
Copilot wants to merge 2 commits intodevfrom
copilot/add-jitter-retry-after-cap

Conversation

Copy link
Contributor

Copilot AI commented Mar 7, 2026

StatusCodeAndExceptionRetry lacked jitter (causing thundering-herd on burst failures), had no awareness of server-sent Retry-After hints, and imposed no upper bound on per-retry sleep duration.

Changes

New builder fields (StatusCodeAndExceptionRetry.java)

  • jitterFactor (double, default 0.0) – when > 0, adds a uniform-random value in [0, baseDelay × jitterFactor] via ThreadLocalRandom; 0.0 preserves deterministic behaviour for existing callers
  • respectRetryAfter (boolean, default false) – when true, parses the Retry-After response header and uses max(computedDelay, retryAfterMs); malformed/absent headers are silently ignored
  • maxTotalDelayMs (int, default 60_000) – hard cap applied after jitter and Retry-After to prevent runaway sleep

New private methods

  • computeDelay(baseDelay, lastResponse) – single point for all delay arithmetic (jitter → Retry-After → cap)
  • parseRetryAfterHeader(response) – supports delta-seconds ("120") and all three RFC 7231 HTTP-date formats using shared static final DateTimeFormatter[] constants (thread-safe; avoids per-call allocation)

attempt() adjustment

Captures lastResponse on each iteration (reset to null on exception paths) so computeDelay can inspect the Retry-After header from the most recent retryable response.

Example

StatusCodeAndExceptionRetry policy = StatusCodeAndExceptionRetry.builder()
    .number(3)
    .initialDelay(500)
    .jitterFactor(0.2)           // ±20% jitter per retry
    .respectRetryAfter(true)     // honour server hints
    .maxTotalDelayMs(30_000)     // never sleep > 30 s per retry
    .isRetryable((r, n) -> r.getStatusCode() == 429)
    .build();

Tests (StatusCodeAndExceptionRetryTest.java, new file)

Covers backward-compatible defaults, retry-count correctness, jitter (zero and non-zero factor), safety-cap enforcement, Retry-After delta-seconds/HTTP-date/past-date/malformed/absent/disabled paths, exception retry path, and getIOExceptionRetryPolicy factory edge cases.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • dl.google.com
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-opens=java.base/java.nio.charset=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED --add-opens=java.xml/javax.xml.namespace=ALL-UNNAMED -XX:MaxMetaspaceSize=384m -XX:+HeapDumpOnOutOfMemoryError -Xms256m -Xmx512m -Dfile.encoding=UTF-8 -Duser.country -Duser.language=en (dns block)
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-opens=java.base/java.nio.charset=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED --add-opens=java.xml/javax.xml.namespace=ALL-UNNAMED -XX:MaxMetaspaceSize=1024m -XX:+HeapDumpOnOutOfMemoryError -Xmx3072m -Dfile.encoding=UTF-8 -Duser.country -Duser.language=en -Duser.variant (dns block)
  • identitydivision.pkgs.visualstudio.com
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-opens=java.base/java.nio.charset=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED --add-opens=java.xml/javax.xml.namespace=ALL-UNNAMED -XX:MaxMetaspaceSize=1024m -XX:+HeapDumpOnOutOfMemoryError -Xmx3072m -Dfile.encoding=UTF-8 -Duser.country -Duser.language=en -Duser.variant (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Original prompt

Add jitter, Retry-After, and safety cap to StatusCodeAndExceptionRetry

Fixes AB#3536278

Repo

AzureAD/microsoft-authentication-library-common-for-android Module: common4j

Description

Enhance StatusCodeAndExceptionRetry with:

  1. jitterFactor (double, default 0.0) delay becomes baseDelay + random(0, baseDelay * jitterFactor)
  2. respectRetryAfter (boolean, default false) parse Retry-After header, use max(computedDelay, retryAfterMs)
  3. maxTotalDelayMs (int, default 60000) per-retry safety cap

Add computeDelay() and parseRetryAfterHeader() private methods. Use ThreadLocalRandom for jitter. Support both delta-seconds and HTTP-date Retry-After formats. Backward-compatible defaults.

Files to Modify

  • common4j/src/main/com/microsoft/identity/common/java/net/StatusCodeAndExceptionRetry.java
  • common4j/src/test/com/microsoft/identity/common/java/net/StatusCodeAndExceptionRetryTest.java (create if needed)

Acceptance Criteria

  • New builder fields with backward-compatible defaults
  • Jitter applied when factor > 0, deterministic when 0
  • Retry-After parsed (delta-seconds and HTTP-date)
  • Malformed Retry-After doesn't crash
  • Safety cap enforced
  • Unit tests cover all scenarios
  • Compiles and tests pass

Dependencies

None

Follow .github/copilot-instructions.md strictly


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

@github-actions
Copy link

github-actions bot commented Mar 7, 2026

✅ Work item link check complete. Description contains link AB#3536278 to an Azure Boards work item.

1 similar comment
@github-actions
Copy link

github-actions bot commented Mar 7, 2026

✅ Work item link check complete. Description contains link AB#3536278 to an Azure Boards work item.

@github-actions github-actions bot changed the title [WIP] Add jitter, Retry-After, and safety cap to StatusCodeAndExceptionRetry [WIP] Add jitter, Retry-After, and safety cap to StatusCodeAndExceptionRetry, Fixes AB#3536278 Mar 7, 2026
Co-authored-by: shahzaibj <37125644+shahzaibj@users.noreply.github.com>
Copilot AI changed the title [WIP] Add jitter, Retry-After, and safety cap to StatusCodeAndExceptionRetry, Fixes AB#3536278 Add jitter, Retry-After respect, and safety cap to StatusCodeAndExceptionRetry Mar 7, 2026
@github-actions github-actions bot changed the title Add jitter, Retry-After respect, and safety cap to StatusCodeAndExceptionRetry Add jitter, Retry-After respect, and safety cap to StatusCodeAndExceptionRetry, Fixes AB#3536278 Mar 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants