Repository navigation
fix(wikis): wait out BWIKI's EdgeOne rate limiting instead of giving up after 3 s - #16
Merged
Merged
Conversation
…up after 3 s The scheduled source check of 2026-10-19 (run 39224451765) failed in discover_bwiki(): BWIKI answered a pages() batch with HTTP 567, EdgeOne's rate limiting, three times in a row. 567 was already retried, but on the same schedule as a server error, 3 attempts 1 s and 2 s apart, so the run stopped about 3 s after the first 567, well inside any rate-limit window. The rate-limit statuses (RATE_LIMITED: 429 and 567) now get RATE_LIMIT_ATTEMPTS (6) calls, waiting 5, 10, 20, 30 and 30 s when the server sends no Retry-After, about 95 s in all. Connection errors, timeouts and 500, 502, 503 and 504 keep 3 attempts 1 s and 2 s apart, so a host that is down still fails fast. A Retry-After still replaces any wait, capped at 30 s. Attempts count across statuses and the latest error decides the limit. tests/test_fetch_wikis.py pins both schedules, the mixed case, and the sentence in docs/sources-wikis.md and its Chinese mirror, computed from retry_delay() and the constants. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AZypKz9iupFY3Dhsd8Q1rK
… URL after it Review of the previous commit found two faults in retried(): one attempt counter served both kinds of error, so a 503 after two 567s was not retried at all, and the rate-limit waits were indexed by the overall attempt, so 503, 503, 567 waited 20 s after the first 567 instead of the documented 5 s. Each kind now has its own counter, its own attempt limit and its own waits; a request is made at most ATTEMPTS + RATE_LIMIT_ATTEMPTS - 1 (8) times, and every failure advances one bounded counter, so the loop always ends. download() tried a Fandom candidate's next URL after any requests error, and each URL now has six rate-limited attempts: a rate-limited candidate would have cost about 3 x 95 s. Rate limiting now fails the candidate at once, as a RefusedDownload does; its URLs are all on the host that limits the rate. A server error still moves on to the next URL. http_status() reads the status for retried(), retry_delay() and download() alike. RetryTests caps the fake web at 20 requests so a broken bound fails instead of hanging; DocumentationTests shares one assertDocumented() helper. Both docs say the kinds are counted apart and that rate limiting ends an image download, and the doc test pins it. Undoing either fix fails the new tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AZypKz9iupFY3Dhsd8Q1rK
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
The scheduled Check wiki sources run of 2026-10-19 (run 39224451765) failed in
discover_bwiki(). BWIKI answered apages()batch with HTTP 567 (EdgeOne rate limiting) three times in a row:567 was already in
RETRY_STATUSES, but it was retried on the same schedule as a server error: 3 attempts, 1 s and 2 s apart. The run stopped about 3 s after the first 567, well inside any rate-limit window.What changes
scripts/fetch_wikis.pyRATE_LIMITED = {429, 567}. These statuses getRATE_LIMIT_ATTEMPTS(6) attempts. When the server sends noRetry-After, the waits are 5, 10, 20, 30 and 30 s, about 95 s in all.Retry-Afterstill replaces any wait, capped at 30 s (unchanged).download()no longer tries a Fandom candidate's other URLs after rate limiting. They're all on the host that is limiting, and without this one candidate could cost about 3 × 95 s. A server error still moves on to the next URL.http_status()is the one placeretried(),retry_delay()anddownload()read the status from.docs/sources-wikis.md,docs/sources-wikis.zh-CN.md: the retry sentence describes both schedules and the separate counting. The Fandom paragraph says rate limiting fails the file at once.tests/test_fetch_wikis.pyretry_delay()and the constants. The doc checks now share oneassertDocumented()helper.RetryTestscaps the fake web at 20 requests, so a broken bound fails instead of hanging.Review
A review of the first commit found that one shared counter let a 503 after two 567s go unretried, and that rate-limit waits didn't follow the documented schedule after other errors. Both are fixed in the second commit. Findings left as they are:
Retry-After: 30could already cause.RequestExceptions: that was already true before this PR.Validation
HTTPError: 567as the CI run. Undoing either review fix also fails them.tests/test_*.pypasses under coverage; total coverage is 89.6% (fail_under = 89).pre-commit runon the changed files passes with the pinned hooks (ruff 0.17.0 check and format, mypy).Caveat
BWIKI isn't reachable from the environment this was written in, so the new backoff hasn't been tried against the live site.
🤖 Generated with Claude Code
https://claude.ai/code/session_01AZypKz9iupFY3Dhsd8Q1rK