Skip to content

chore: replace always-on 429 retry with opt-in RetryConfig - #56

Merged
mogita merged 3 commits into
masterfrom
feat/cha-2959-retry
Jul 24, 2026
Merged

chore: replace always-on 429 retry with opt-in RetryConfig#56
mogita merged 3 commits into
masterfrom
feat/cha-2959-retry

Conversation

@mogita

@mogita mogita commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

Ticket

https://linear.app/stream/issue/CHA-2959/rate-limits-and-retry

Summary

Adds an opt-in auto-retry policy. RetryConfig is disabled by default (maxAttempts=3, maxBackoff=30s). When enabled, retries ONLY GET/HEAD requests that fail with HTTP 429 or a transport error, honoring Retry-After (clamped to maxBackoff) otherwise exponential backoff with full jitter. Never retries writes or 5xx; always honors the backend unrecoverable flag; surfaces the last attempt's error. Config surface: ClientBuilder::retry(new RetryConfig(...))

Notes

  • Breaking: replaces the SDK's previous always-on 429 retry loop; out of the box the client no longer auto-retries (a 429/transport error surfaces to the caller). Opt back in via RetryConfig.
  • Retry attempts are observable via the existing http.request.failed log event with a retry.attempt field (transport retries carry error.type; 429 retries omit it, since that field is the transport-only enum).
  • Titled chore: intentionally so the merge does not auto-publish (despite the breaking change). Publish later via the manual Release workflow; the release notes should flag the breaking behavior change.

mogita added 2 commits July 23, 2026 14:37
The client no longer retries HTTP 429 responses automatically. Add
RetryConfig (disabled by default) and ClientBuilder::retry() to opt in.
When enabled, only GET/HEAD requests retry on 429 (unless unrecoverable)
or a transport error, honoring Retry-After and otherwise backing off
exponentially with full jitter, both capped at maxBackoff. A DEBUG
http.request.failed event with a retry.attempt field is emitted before
each retry; the existing ERROR-level event for a surfaced transport
failure is unchanged.

GuzzleHttpClient's $maxRetries constructor param is now deprecated and
ignored, kept only for positional-call compatibility.
…ited

error.type is a closed transport-failure enum (connection_reset | timeout |
dns_failure | tls_handshake_failed | unknown). A 429 is a received response,
not a transport failure, so emitRetryAttempt no longer fabricates a
rate_limited value for it; the field is only set when the retried error is a
StreamTransportException.
testHardDeleteChannels waits on an async hard-delete task, but under shared-backend async-queue latency it can miss the 120-attempt poll window and fail. Add an opt-in skipOnTimeout to waitForTask so the hard-delete test skips rather than fails on timeout, mirroring the getstream-go fix; a genuine task failure still surfaces as status 'failed', and other callers keep the default fail-on-timeout.
@mogita
mogita merged commit b562a75 into master Jul 24, 2026
7 checks passed
@mogita
mogita deleted the feat/cha-2959-retry branch July 24, 2026 14:03
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.

1 participant