feat: add async retry and bearer-auth pipeline steps#157
Conversation
Add the AsyncHttpStep counterparts for the RETRY and AUTH pillar stages so async calls get the same retry and authentication behaviour as the synchronous pipeline. DefaultAsyncRetryStep mirrors DefaultRetryStep's policy exactly — it reuses HttpRetryOptions, the shared BackoffCalculator, RetryAfterParser, the same Retry-After header set, and the same idempotency-aware re-sendability gating. Backoff delays are scheduled on a ScheduledExecutorService via Futures.delay rather than blocking a thread, and the retry loop is driven by an iterative trampoline (no per-attempt thenCompose recursion), so a long zero-delay retry sequence stays stack-safe. AsyncBearerTokenAuthStep stamps Authorization: Bearer via a new non-blocking BearerTokenProvider.fetchAsync seam. A token that is still valid but inside the refresh margin is returned and stamped immediately while a refresh runs off-thread; concurrent requests that observe an expiring or missing token share a single in-flight fetch (single-flight) so they don't stampede the token endpoint. The HTTPS guard, cross-origin credential suppression, and 401-challenge token eviction match the synchronous BearerTokenAuthStep. A ManualScheduler test fixture drives the scheduled delays deterministically so the retry tests run without real sleeps.
|
This adds the async ( IssuesBlocking
Nit
|
Summary
Adds the async (
AsyncHttpStep) counterparts for the RETRY and AUTH pillarstages, so calls run through the async pipeline get the same retry and authentication
behaviour the synchronous pipeline already has.
Async retry (
DefaultAsyncRetryStep,AsyncRetryStep)Mirrors
DefaultRetryStep's policy exactly — it reusesHttpRetryOptions, the sharedBackoffCalculator,RetryAfterParser, the sameRetry-Afterheader set, and the sameidempotency-aware re-sendability gating (idempotent method or replayable body; a bare
non-idempotent POST or a non-replayable body is not retried).
ScheduledExecutorServiceviaFutures.delay— noThread.sleep, noTimer. While a delay is pending thedispatching thread is free.
per-attempt
thenComposechain, so a long (even zero-delay) retry sequence does notgrow the call stack or the future continuation graph. A 5,000-attempt test proves this.
CompletionExceptionwrappers are unwrapped before classification;Erroris never retried; prior attempts are attached as suppressed on the terminalexception.
Async bearer auth (
AsyncBearerTokenAuthStep,AsyncAuthStep)Adds a non-blocking
BearerTokenProvider.fetchAsyncseam (default forwards to the blockingfetch; adapter modules override to dispatch off-thread). The step:returned and stamped immediately while the refresh runs off-thread — the in-flight
request never waits on the token endpoint.
one in-flight provider call, so they don't stampede the token endpoint.
401 +
WWW-Authenticate: Bearertoken eviction + single-retry behaviour from thesynchronous
BearerTokenAuthStep.Per-cloud token providers (GCP/Azure/Kubernetes) and OAuth token-exchange specifics are
deliberately kept out of
sdk-core— they belong in adapter modules and overridefetchAsync.Testing
A new
ManualSchedulertest fixture drives the scheduled delays deterministically, so theasync retry tests assert the full backoff/Retry-After schedule with no real sleeps. The
async auth tests use deferred
CompletableFutures to prove the valid-but-expiring token isstamped without blocking and that concurrent fetches coalesce.
Closes #31
Closes #32
Gated build (module-scoped, run locally)
All passed.
apiDumpwas run for the intentional public-API additions and the regeneratedapi/sdk-core.apiis committed.