Skip to content

retrylens

Preview the November 2026 AWS SDK for Java retry-behavior changes in your own app — before AWS flips the default in production.

Java 17+ AWS SDK v2 License: Apache-2.0 CI

The problem

In November 2026, AWS is changing the default retry behavior across every AWS SDK — Java, Python, JS, Go, PHP, and the CLI (announcement). If you do nothing, your services will start behaving differently in production:

Setting Legacy default 2026 default
Max attempts 4 3
Base delay 100 ms 50 ms
Throttling delay 500 ms 1000 ms
DynamoDB attempts 9 4
Retryable errors + LimitExceededException, IdpCommunicationErrorException

Most teams can't answer the only question that matters: "what would this mean for my app?" They can't opt in blindly and they can't wait until November to find out.

What retrylens does

It attaches a single ExecutionInterceptor to any AWS SDK v2 client, records every attempt in a bounded ring buffer, and answers three questions your logs can't:

  1. What is my SDK actually doing right now? — per-operation attempts, retries, throttles, failures, p50/p95 wall-clock.
  2. What would the November 2026 defaults do to that traffic? — a pure-function simulator replays each recorded call under the new rules and reports the diff.
  3. How does my code react when the SDK decides to stop retrying? — a deterministic FailureInjector throws service exceptions the SDK's own retry loop treats as real.

No re-issuing of requests. No AWS calls. No credentials required to try it.

Before / after

Before — you push to prod on Nov 1st and hope:

DynamoDbClient ddb = DynamoDbClient.create();
// … your app …

After — one line, and you know:

RetryLens lens = RetryLens.create();

DynamoDbClient ddb = DynamoDbClient.builder()
    .overrideConfiguration(o -> o.addExecutionInterceptor(lens.interceptor()))
    .build();

// … exercise your app on real traffic for a while …

RetryReport now = lens.report();
RetryReport in2026 = now.previewFor(RetryMode.STANDARD_2026);

System.out.println(now.diff(in2026).totalFailuresDelta() + " extra failures under 2026 defaults");
System.out.println(in2026.toText());

Sample output:

retrylens report — generated 2026-08-22T18:04:11.923Z
  calls=1428  attempts=1837  retries=409  throttled=118  failures=6

  operation                                 calls  attempts   retries  throttled  failures         p50         p95
  ----------------------------------------------------------------------------------------------------------------
  DynamoDb.PutItem                            310       498       188         12         2         31ms       410ms
      top errors: ProvisionedThroughputExceededException=12, InternalServerError=3
  S3.GetObject                                812       912       100          0         0          8ms        41ms
  Sqs.ReceiveMessage                          201       253        52         83         3         21ms      2.10s
      top errors: RequestThrottled=83

Install

Maven:

<dependency>
    <groupId>io.github.bibekmhj</groupId>
    <artifactId>retrylens</artifactId>
    <version>0.1.0</version>
</dependency>

Gradle:

implementation("io.github.bibekmhj:retrylens:0.1.0")

retrylens has zero required runtime dependencies — the AWS SDK is declared provided, so you keep whichever version you already use (2.20+ recommended, and the simulator is calibrated for the values in SDK 2.44's AWS_NEW_RETRIES_2026 announcement).

Quick start (30 seconds)

import io.github.bibekmhj.retrylens.RetryLens;
import io.github.bibekmhj.retrylens.RetryMode;
import software.amazon.awssdk.services.s3.S3Client;

RetryLens lens = RetryLens.create();

S3Client s3 = S3Client.builder()
    .overrideConfiguration(o -> o.addExecutionInterceptor(lens.interceptor()))
    .build();

// … app runs …

System.out.println(lens.report().toText());
System.out.println(lens.report().previewFor(RetryMode.STANDARD_2026).toText());

Features

  • 2026 preview simulator — pure function, replays observed traces under the new defaults. No live retries, no AWS calls.
  • Per-operation report — attempts, retries, throttles, failures, p50/p95, top error codes.
  • Ring-buffered trace store — bounded memory footprint, configurable capacity.
  • Failure injector — deterministic ExecutionInterceptor that throws real AwsServiceExceptions from beforeTransmission. Lets you test how your app reacts to throttling or transient errors without provoking AWS.
  • Micrometer bridge (optional) — turn every trace into retrylens.apicall / retrylens.attempts / retrylens.retries / retrylens.throttled / retrylens.failures meters. Fills the gap of aws-sdk-java-v2#4611.
  • JSON output — hand report.toJson() to dashboards or CI assertions with no extra dependency.

The 2026 preview, in more detail

RetryReport.previewFor(RetryMode.STANDARD_2026) returns a new immutable report that answers, per call:

given the same per-attempt outcomes we recorded, would the November 2026 defaults have retried this? For how long? Would it have finally succeeded, or would the smaller attempt budget have exhausted the retries?

The simulator applies AWS's documented rules — smaller attempt caps, revised backoff timing, added retryable exceptions, DynamoDB's 9-→-4 attempt drop — and never re-issues a request. Safe to run against production traffic. See docs/2026-RETRY-CHANGES.md.

Compatibility

Component Supported
Java 17, 21, 22, 23
AWS SDK for Java v2 2.20 and newer (retry values calibrated for 2.44+)
Spring Boot 3.x (bring your own configuration; example provided)
Micrometer 1.10+ (optional)

AWS SDK for Java v1 reached end-of-support on Dec 31 2025 and is out of scope.

Testing without an AWS account

The FailureInjector interceptor throws real AwsServiceExceptions from beforeTransmission — the SDK's retry engine treats them exactly like service errors:

FailureInjector injector = FailureInjector.builder()
    .rule(FailureInjector.rule()
        .whenService("DynamoDb").whenOperation("PutItem")
        .times(2)
        .throwThrottling("ProvisionedThroughputExceededException"))
    .build();

DynamoDbClient ddb = DynamoDbClient.builder()
    .overrideConfiguration(o -> o
        .addExecutionInterceptor(lens.interceptor())
        .addExecutionInterceptor(injector.interceptor()))
    .build();

Combine with RetryLens to see exactly what the SDK — and, via the simulator, what the 2026 defaults — would do.

Architecture

See docs/ARCHITECTURE.md.

The core is one interceptor and one immutable record type. Everything else is a pure function of a list of recorded traces. That is why the preview mode is safe to run against production traffic and why the same code path produces the report, the JSON output, and the Micrometer meters.

Roadmap (subject to what users ask for)

  • Spring Boot starter with autoconfiguration.
  • Additional simulator modes (adaptive retry rate-limiting).
  • CLI diagnostic: java -jar retrylens.jar --scan against a running JMX endpoint or heap dump.
  • Real OpenTelemetry Observation API integration when AWS ships the SPI.

Why does this exist?

Because "test in prod on November 1st" is not a plan, and because reading AWS's changelogs is not the same as knowing what your services will do. retrylens is a very small piece of code that turns a nervous multi-quarter migration into a diff you can read in your terminal today.

If AWS SDK v2 later adopts a first-class 2026 preview mode of its own, this library will happily deprecate itself. Until then, the goal is: one dependency, one interceptor, one report, one question answered.

License

Apache 2.0. See LICENSE.

Contributing

Bug reports, ideas, and PRs welcome. See CONTRIBUTING.md and CODE_OF_CONDUCT.md.

References

About

See every AWS SDK for Java v2 retry — and preview the November 2026 default changes before they hit prod.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages