Skip to content

Handle Retry-After on every retryable status, including 529 - #148

Open
MichaelGHSeg wants to merge 4 commits into
csharp-public-httpconfigfrom
csharp-529-retry-after
Open

Handle Retry-After on every retryable status, including 529#148
MichaelGHSeg wants to merge 4 commits into
csharp-public-httpconfigfrom
csharp-529-retry-after

Conversation

@MichaelGHSeg

Copy link
Copy Markdown
Contributor

Stacked on #147 — review that first; this PR's diff is the single 529 commit.

Why

We were asked to add 529 support with Retry-After headers. The conclusion was to check Retry-After on every retryable status rather than special-casing 429 — 529 is simply one more member of the retryable set.

This is the same change already shipped in analytics-java 3.5.5, and matches the generic-retry-after conformance suite merged in sdk-e2e-tests#16.

What

  • Any retryable response carrying a valid Retry-After routes through the rate-limit path, which does not consume retry budget.
  • Retryable statuses without Retry-After continue to use counted exponential backoff.
  • Adds RetryAfterParser, handling both delta-seconds and RFC 1123 HTTP-date forms (returning null for dates in the past).

Testing

  • 229 unit tests pass on the combined branch (this commit plus Expose HttpConfig so retry behaviour is user-configurable #147), including new coverage for 503/529 with and without Retry-After, and clamping to maxRetryInterval.
  • Full e2e suite passes locally: 79 passed / 0 skipped across 11 files, including the 11-test generic-retry-after (529) suite and all four retry-settings suites.

E2E was run locally because CI cannot currently check out the private sdk-e2e-tests repo — the token: ${{ secrets.E2E_TESTS_TOKEN }} line was dropped from that checkout step during CI hardening. That's tracked separately; note the same omission is present in this repo's ci/harden-build-publish branch and in go/ruby/php.

Related

The equivalent change is open for the other SDKs: analytics-python#520, analytics-go#206, analytics-ruby#280, analytics-php#251.

Route any retryable response carrying a valid Retry-After header through the
rate-limit path (no retry-budget cost) instead of special-casing 429. Retryable
statuses without Retry-After continue to use counted exponential backoff. Adds
529 to the retryable set and covers both paths with tests.

Matches the behaviour already shipped in analytics-java 3.5.5 and the
generic-retry-after conformance suite in sdk-e2e-tests.
Routing any retryable status with Retry-After to the rate-limit path left
ShouldDeleteBatch inconsistent with HandleResponse. With rate limiting on and
backoff off, a 503 or 529 carrying Retry-After would rate-limit the pipeline
(WaitUntilTime set, uploads blocked) while ShouldDeleteBatch still reported
true, so the batch file was deleted and the pipeline then stalled waiting to
retry events that no longer existed.

That configuration is reachable from CDN settings and directly from
Configuration.HttpConfig — it is the config ConfigurationHttpConfigTest builds.

ShouldDeleteBatch now keeps a retryable batch whenever rate limiting is
enabled, matching swift's shouldDropBatch ("Rate limit config handles retryable
codes that carry Retry-After — don't drop"). Non-retryable statuses are still
dropped, and a retryable status with neither rate limiting nor backoff enabled
is still dropped since nothing would retry it.
The previous commit kept a retryable batch whenever rate limiting was enabled,
which was too broad: a 500 with no Retry-After and backoff disabled was also
kept, so the file was re-uploaded even though nothing had scheduled a retry.
The sdk-e2e-tests "backoffConfig.enabled: false" case caught this — it expects
exactly one request and saw two.

ShouldDeleteBatch now takes the same retryAfterSeconds value handed to
HandleResponse, so the two agree on whether the response actually took the
rate-limit path. A retryable status keeps its batch only when it carries a
usable Retry-After and rate limiting is on; otherwise only backoff can retry
it, and with backoff off the batch is dropped as before. The single-argument
overload is retained.

232 tests pass.
Analytics-CSharp-plan.md states 'Spec item 1: 2xx and 3xx are success', but
IsSuccessStatusCode and the two status checks in RetryStateMachine were
2xx-only, so a 3xx fell through to the retry classifier. analytics-go,
analytics-python and analytics-php already follow the spec here; this brings C#
into line with them and with its own plan.

236 tests pass, including new cases covering 200, 201, 301 and 304.
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