Repository navigation
feat(client): retry only failures another try can fix, with jittered backoff - #62
Draft
gonzalo-zenrows wants to merge 1 commit into
Draft
gonzalo-zenrows wants to merge 1 commit into
gonzalo-zenrows wants to merge 1 commit into
Conversation
…backoff A cold Extract request can briefly answer 503 with "retry in a minute, not charged", and many clients retrying on the same fixed schedule hit the API again in lockstep. The client now retries by default and spreads its retries out. - retries now defaults to 2 (was 0); retries=0 turns retries off. - Retried: 429, 502, 503, 504 (none of them is charged) and a failure to connect. No longer retried: 500 and 422. Never retried: a read timeout, because the server may still finish, and charge, the request. - Waits honour Retry-After, capped at retry_max_wait (default 30 s), or else a random 0.25 s to min(retry_max_wait, 2 ** attempt) s. - retry_budget (default 90 s) stops retrying when the next wait would end that long after the call's first try. - The separate 422 retry loop in _worker is gone: one retry layer. - urllib3 >= 1.26 is now declared, for Retry(allowed_methods=...).
Author
|
Same retry policy across all clients (429/502/503/504 and pre-response connection errors; 2 retries by default; full jitter; Retry-After capped at 30 s; 90 s total budget; timeouts never retried):
|
This was referenced Oct 16, 2026
Draft
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
When a request fails in a way that another try can fix,
ZenRowsClientshould wait and try again, not fail at once and not hit the API again on a fixed schedule. For example, a cold Extract request can briefly answer503with "retry in a minute, not charged". Today retries are off by default. When they are on, every client waits the same fixed time, so many clients retry together.What changed
ZenRowsClientnow has one retry policy, the same as the Go SDK, Node SDK and CLI PRs that ship with it:429,502,503,504, and a failure to connect (connection refused, DNS failure, connect timeout). The API does not charge any of these statuses.500and every422(both were retried before), and a read timeout. After a read timeout the server may still finish the request and charge it, so a retry could charge it twice.Retry-After, capped atretry_max_wait. Without it, a random time between 0.25 s andmin(retry_max_wait, 2 ** attempt)s ("full jitter": attempt 0 waits up to 1 s, attempt 1 up to 2 s, and so on). An unreadableRetry-Afterfalls back to the random wait.retry_budgetseconds after the call's first try. The first try's own duration counts.422retry loop in_workeris removed, so there is one retry layer: a urllib3Retrysubclass (_JitteredRetry) mounted on the session. It overrides only hooks that urllib3 1.26 and 2.x share.pyproject.tomlnow declaresurllib3>=1.26, becauseallowed_methodsneeds it.extract(mode="auto")andextract_asyncare unchanged: a403 REQS007or402 AUTH010still falls back once to Autoparse. A503is retried before the fallback check sees the final response.requests.exceptions.ReadTimeout, as before.New defaults and how to opt out
retries0(off)2retry_max_wait(new, keyword-only)30.0seconds: cap for one waitretry_budget(new, keyword-only)90.0seconds: limit for the whole callTo keep the old behaviour of no retries, pass
retries=0:Callers who already set
retrieskeep their retry count and get the new statuses and waits. This is afeatbecause the default changes. release-please will make it a minor release with a changelog entry.Tests
tests/test_retries.pynow runs the client against a local HTTP server, so each test exercises urllib3's real retry path. It covers:503Retry-Afterhonoured, and capped at 30 s and atretry_max_wait400,401,404,422(both bodies),500, and413withRetry-AfterPOSTretriedretries=0turning retries offResults:
make check: passed.make test: 275 passed, on urllib3 2.8.0.