Repository navigation
Nothing removes a cached record #46
Closed
robert-dean
started this conversation in
Ideas
Replies: 1 comment
|
Closing: the substance is built. The record cache has its cap and sweep, one record can be cleared from the console ( The only item left is a size cap for art and segments, which this discussion already filed as one to watch rather than fix. |
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Status: built, 2026-08-16. What is left is at the bottom.
Written: 2026-08-14, when
playout.trackCachewas removed and keeping every record becameunconditional.
State of the tree then:
TrackAudioServicewrote every record it fetched intoTRACKS_DIRthrough
TrackStore, content-addressed, anddeadair.track_audioheld one row per binding sayingwhich file. Nothing ever deleted either.
TRACKS_DIRgrew for as long as the station ran.Why the switch that used to cover this is gone
playout.trackCachewas a boolean: off meant the station neither served from the cache nor filledit, and a fetched record went into a small bounded in-memory hold instead of onto disk. It was
removed rather than defaulted, because its off state stopped being expressible. A record may not
enter the committable window until its audio is on this machine (
docs/internals/director.md§"Nothing airs until its bytes are here"), so a station keeping nothing would have nothing ready and
would never commit anything at all — the switch had become a way to make the station silent.
What it was actually reached for was A/B-ing a suspected bad cached file, and that is better served
by deleting the file:
TrackAudioService.locatealready treats a row claiming bytes the disk has notgot as a re-fetch, and repairs the row on the way through. There is now a button for exactly that;
see below.
So the disk cost is now unconditional, and this file was the bill.
What it costs, measured against this install
A 320kbps Ogg is roughly 8–10 MB for a four-minute record; a FLAC from a Navidrome library is 25–40
MB. At 581 catalog tracks that is somewhere between 5 GB and 20 GB for the whole library, which is
not alarming — the shape of the problem is not the current catalogue, it is that discovery has no
ceiling.
PickResolver.identifyingests records from providers that were never in a playlist(
docs/internals/programming.md, the discovery rule), so the set of records the station can fetch is the provider'scatalogue rather than the operator's library.
Measured again on the day it was built: 919 catalog tracks, 345 of them held, 3.5 GB. The library had
grown by 58% in two days, which is the ceiling argument arriving rather than a surprise.
What was built
playout.trackCacheMaxBytes, unset by default, and a sweep that drops the least recently playedrecords until the total is under it. Three things about the shape it took.
The cap is the trigger and age is only the order. An under-cap run reads one aggregate and stops,
so a station inside its cap is untouched however often the sweep fires. That is what made the "no
schedule" rule below survivable in the form it took.
It runs as its own job,
playout.sweep_track_cache, every fifteen minutes — NOT off the commitpass, which was the obvious place and is wrong twice over: that loop keeps the running order full
and is the one an operator hears when it is slow, and the ripener's subject is the fetch window
rather than the disk. What the ripener keeps is one line,
TrackAudioService.protect, publishing therecords the sweep may not touch. That is also how the committable-window rule is honoured without
playoutdepending ondirector, which would be a cycle — the director already imports the planner.last_served_atwent in first, on its own, as this file said it had to. It isnot nullwith anow()default, which the file did not predict and which is the better shape: a null would be a rowthe sweep has to have an opinion about, and the honest default for a record fetched and not yet
served is the moment it arrived. That also makes the index a plain btree with no coalesce and no
nulls-first trap. It is written on a cache HIT and nowhere else — deliberately not in
readyFor,which stats the same window every few seconds and would report the whole forward order as freshly
served, flattening the ordering the sweep depends on.
Three rules it has to hold — all three held
described above, and the sweep reports being stuck over the cap rather than forcing its way under.
TrackAudioService.inFlight, read fresh on every round ofthe sweep rather than once, because a fetch that finishes mid-sweep is a record that must not be
taken.
GET /storagerather than a sweep-timereport, which is better: the numbers are on the settings page instead of in a log nobody reads.
Report and never repair, as the rule said. It found 151 unclaimed art files and 125 unclaimed
segment files on its first run.
One thing the file did not anticipate: a file two bindings share must not be deleted for one of
them. The store is content-addressed, so identical audio is one file, and the sweep clears the rows
first and then deletes only the checksums no surviving row still references.
What is deliberately not proposed — still not
A size cap per record.
MAX_TRACK_BYTESalready refuses anything over 64 MB by serving nothingat all rather than by truncating.
Evicting on a schedule. The sweep runs on a cron and this rule survives intact, because what it
refuses is evicting on AGE. The cap is what decides whether anything happens at all.
What is left
has a limit or a sweep. Both are far smaller and neither has discovery's open ceiling, so this is a
bill to keep an eye on rather than one to pay.
nothing. Doing something about them needs a rule about what a file with no row means when a write
was interrupted a second ago rather than a week ago, and that rule wants the numbers this now
produces.
DELETE /catalog/tracks/{id}/audio), includingthe refusal inside the committable window. See track-state-console.
Moved from
docs/todo/track-cache-eviction.md. The file as it last stood: https://github.com/robert-dean/deadair/blob/71e431d45ce7b35a189c7d226832ec4a6247e9dc/docs/todo/track-cache-eviction.mdAll reactions