Files
LEDMatrix/test
ChuckandClaude Opus 5 ff8d0b01a6 fix(memory): release fetched payloads once they have been delivered
BackgroundDataService kept the fetched body on the FetchResult it filed
in completed_requests, which is swept hourly and capped at 500 entries
by count. For a status record that costs nothing; for a season schedule
it costs a tenth of the board.

Measured on a 1GB Pi 3B+ with a 1-second RSS profile: the display
process sat at 404MB after plugin load, then stepped +21MB when NFL
fetched its season and +90MB when NCAA football fetched 946 games for
2026 -- and stayed at 494MB. Not a leak; a staircase that never came
down. When a later fetch landed while headroom was low, available memory
reached ~70MB, fork() began failing, and the board stopped being able to
start a process at all: sshd accepted connections and closed them before
its banner, systemd could not respawn the display, and the panel went
dark while the kernel carried on answering pings.

The cache-hit path was the worse of the two. It runs once per update
interval per sport, mints a fresh request_id each time, and files
whatever the cache returned. The memory tier is capped at 150 entries on
a 1GB board, so a miss re-parses the payload from disk into a genuinely
new object -- separate copies accumulating toward the 500-entry cap,
not shared references.

Releasing is safe: the payload is written to the cache under the
request's cache_key before the result is built, the callback is handed
the object directly, and consumers read it back from the cache
afterwards (the plugins' callbacks use it only in passing, to log a
count, before reading the cache). Nothing is lost -- it moves from RAM
to the disk cache that was already holding it.

Requests submitted without a callback keep their payload, since polling
get_result() is then the only way to collect it. That keeps the existing
contract, and the existing tests covering it, intact.

Not addressed here: max_workers=3 allows three concurrent fetches, so
three large parses can peak at once, and there is no in-flight dedupe by
cache_key -- a second submit for a key already being fetched starts a
second fetch. Both bound the transient peak rather than what stays
resident, and both are behaviour changes worth their own review.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-23 12:38:27 -04:00
..