Files
last30days-skill/tests
Gabriel Arrillaga 5a2fe5279b fix(http): expand retry budget + use exponential backoff on DNS failure
Transient DNS resolution failures (socket.gaierror, surfaced as
urllib.error.URLError with reason=gaierror) were retried with the
generic URLError handler — linear backoff (2s, 4s, 6s) and bounded by
the caller-passed `retries` parameter. For callers that pass small
retry values (e.g. lib/reddit.py::_subreddit_search uses retries=2), a
single first-attempt DNS hiccup followed by one quick retry on the
still-flaky resolver would exhaust the retry budget and wipe a whole
subreddit sweep — which the caller's broad `except Exception` then
silent-empties as `[]`.

Fix:
- Distinguish URLError-with-gaierror-reason from generic URLError via
  a new `_is_dns_failure()` helper.
- For DNS failures, use exponential backoff (1s, 2s, 4s, ...) instead
  of the linear default.
- For DNS failures, expand the effective retry budget to at least
  MIN_DNS_RETRIES (=3) on first occurrence, so callers that passed
  `retries=2` still get a meaningful retry budget for the transient
  case. Non-DNS URLErrors and HTTPErrors keep the caller's value.
- DNS attempts are counted separately (`dns_attempts`) so unrelated
  URLError or OSError failures within the same call don't accidentally
  expand the budget further.

Reported during a community-signal pass where the Reddit subreddit
sweep silently returned zero items after a first-round transient DNS
hiccup. The fix lives at the http layer (where the retry loop is)
rather than per-source so every caller benefits.

Tests:
- Verifies a caller-passed retries=2 still gets MIN_DNS_RETRIES=3
  attempts on gaierror.
- Verifies gaierror-then-success returns successfully on attempt 2.
- Verifies the exponential-backoff sleep pattern (1s, 2s) on the
  retry attempts before exhaustion.
- Verifies a non-DNS URLError (ConnectionRefusedError reason) does
  NOT expand the retry budget — only true DNS failures do.

All 12 http tests pass (8 baseline + 4 new). No regressions in the
broader test suite (1373 pass / 14 fail, vs 1369 pass / 14 fail on
main — the 14 failures are pre-existing and unrelated to this PR).
2026-05-16 20:54:14 -07:00
..
2026-01-23 12:37:31 -08:00
2026-04-23 20:15:02 -04:00
2026-04-23 20:15:02 -04:00
2026-04-23 20:15:02 -04:00
2026-04-23 20:15:02 -04:00
2026-04-23 20:15:02 -04:00
2026-04-23 20:15:02 -04:00
2026-04-23 20:15:02 -04:00
2026-04-23 20:15:02 -04:00
2026-04-23 20:15:02 -04:00
2026-04-23 20:15:02 -04:00
2026-04-23 20:15:02 -04:00
2026-04-23 20:15:02 -04:00
2026-04-23 20:15:02 -04:00
2026-04-23 20:15:02 -04:00
2026-04-23 20:15:02 -04:00
2026-04-23 20:15:02 -04:00
2026-04-23 20:15:02 -04:00
2026-04-23 20:15:02 -04:00
2026-04-23 20:15:02 -04:00
2026-04-23 20:15:02 -04:00
2026-04-23 20:15:02 -04:00
2026-04-23 20:15:02 -04:00
2026-04-23 20:15:02 -04:00
2026-04-23 20:15:02 -04:00
2026-04-23 20:15:02 -04:00
2026-04-23 20:15:02 -04:00
2026-04-23 20:15:02 -04:00
2026-04-23 20:15:02 -04:00
2026-04-23 20:15:02 -04:00
2026-04-23 20:15:02 -04:00
2026-04-23 20:15:02 -04:00
2026-04-23 20:15:02 -04:00