A soccer board with eight leagues logged ~90 NameResolutionErrors for
site.api.espn.com within a minute of every start on ledpi, plus
"update() timed out". ESPN rejects date ranges, so every league's
fortnight-either-side window became 29 day requests, several managers
fetched it at once, and each window got its own six chunk threads:
~450 requests per start with ~45 in flight, each request past a
session's pool a new connection and a new DNS lookup.
- fetch_espn_date_chunks() asks for a partial edge month whole once the
window covers ESPN_MONTH_COVER_MIN_DAYS (7) of its days, and trims the
answer to the window by each event's US Eastern start date, the day
ESPN's dates=YYYYMMDD means (417/417 live soccer events matched). A
29-day window over two months is 2 requests, not 29. A live poll's
1-2 days stay day requests. A capped trimmed month re-asks only the
window's days; an undatable event is kept. New espn_request_chunks().
- Chunk requests share one process-wide cap of ESPN_CHUNK_WORKERS in
flight instead of six per window.
- A new process starts as if a range had just been rejected, so it no
longer spends a doomed 400 per window at every start.
Live ESPN, soccer-scoreboard 2.39.2, alternating runs: ~450 -> 46
requests per start, peak ~45 -> 13 in flight, ~75 -> 30 DNS lookups.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fetch service stage 2: one ESPN scoreboard cache key shared across the sports base classes (legacy keys still read), and a max-age response cache in the fetch service.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Core's own HTTP fetch paths (APIHelper, fetch_espn_scoreboard and its date chunks, BackgroundDataService, BaseOddsManager.get_odds) go through one service in src/common/fetch_service.py: shared connection pools per retry policy, merged identical in-flight GETs, per-host token-bucket budgets (fetch_service.rate_limits), and per-plugin request counters published to GET /api/v3/plugins/fetch-stats. Return values, exceptions, cache keys, TTLs and retry policies are unchanged. Core-internal in this release; plugins should not import it directly yet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>