From f7e5a6a4d07a78288e731e7043ce37c8203b67d8 Mon Sep 17 00:00:00 2001 From: Chuck <33324927+ChuckBuilds@users.noreply.github.com> Date: Fri, 18 Sep 2026 08:28:08 -0400 Subject: [PATCH] fix(sports): drop capped month payloads before fetching their days Review of the concurrent chunk fetch found it raised the worst-case peak memory more than the concurrency explains. The old loop discarded a month that came back at the 500-event cap the moment it saw it; the rewrite kept every capped month alive in `results`/`slots` until all of their day requests had finished. Measured on a Pi 4 fetching 20260201-20260531 college baseball (four capped months, 5462 events), peak RSS growth over the call: sequential (main) 83 MB concurrent, months retained 121 MB (+43) concurrent, one worker 108 MB -- the retention alone was +25 concurrent, months dropped 98-100 MB (+16) docs/LOW_MEMORY_BOARDS.md puts a 1 GB Pi 3B+ at under 200 MB of headroom, where running out makes the board unreachable until a power cycle, so the difference matters. The remaining +16 MB is six responses parsing at once; three workers saved about 6 MB more, within run-to-run noise, so the worker count stays at six. Co-Authored-By: Claude Opus 5 --- src/common/espn_dates.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/common/espn_dates.py b/src/common/espn_dates.py index 9a150a4e..871378d7 100644 --- a/src/common/espn_dates.py +++ b/src/common/espn_dates.py @@ -272,9 +272,10 @@ def fetch_espn_date_chunks( # A month that came back at the cap is truncated; its days replace it in # place, so merged events stay in chunk order however the requests raced. - slots: List[Any] = list(results) + slots: List[Any] = results capped: Dict[int, List[str]] = {} - for index, (chunk, payload) in enumerate(zip(chunks, results)): + for index, chunk in enumerate(chunks): + payload = slots[index] if payload is None or len(chunk) != 6: continue events = payload.get("events") if isinstance(payload, dict) else None @@ -285,6 +286,13 @@ def fetch_espn_date_chunks( chunk, ESPN_MAX_LIMIT, ) capped[index] = _days_of_month(chunk) + # Drop the truncated month now rather than after its days arrive: + # a capped college-baseball month is ~2MB of parsed JSON, and + # holding four of them through ~120 day requests added ~25MB to + # the peak -- more than the concurrency itself. Low-memory boards + # (docs/LOW_MEMORY_BOARDS.md) have under 200MB of headroom. + slots[index] = None + payload = events = None if capped: days = [day for index in sorted(capped) for day in capped[index]]