Skip to main content

Module exploded_cache

Module exploded_cache 

Source
Expand description

The exploded tier is a CACHE, not a tier — and this is the bound.

Rickard, 2026-08-30, on being shown the lazy-explode design: “does lazy explode mean it eventually takes the disk anyway?” — and the honest answer is yes, unless the tier is capped, because a client that browses a whole history reads every object once and a write-back-on-first-read tier would then hold exactly what the eager one held. MEASURED on the rig 2026-08-30: the 8.12 GiB torvalds/linux pack drove the eager tier to 141.7 GiB into /data, ~17× the pack. “We cannot have one repo take all 300 GB disk.”

So the tier has a byte budget and evicts. Nothing here is data: every row is re-derivable from the verbatim pack bytes by crate::resolve::materialize_at, so an eviction is never a loss and never an error a client can see — it is a later read paying ~0.5–2 ms to inflate its own delta chain again.

§Two pools, and the split is the whole design

  objects.exploded         the EAGER pool — what the indexer writes at push
                           time. Under the shipped policy that is commits
                           and tags: what `graph`/`reach`/tag-peel need, so
                           visibility and pull checks never stall.
                           NEVER evicted — it is the "commits last" rule,
                           held by construction rather than by a comparator.

  objects.exploded.c1      the LAZY pool — first-read write-backs, appended
  objects.exploded.c2      in read order, one file per segment.
  …                        EVICTION IS ONE `unlink` of the oldest segment.

Both pools are counted against the budget and both are named on the dashboard; only the lazy one is evictable. If the eager pool alone exceeds the budget the lazy pool is evicted to nothing and the eager pool is still kept — a store that cannot answer reach is broken, and a store that reads slowly is not.

ExplodedArchive is one append-only Arrow IPC stream: [schema][batch]…, nothing ever rewritten. That is what makes it 1.0× its payload instead of redb’s 12×, and it is also what makes deleting a row in the middle cost a whole-file rewrite — up to 17 GB, which the module docs already refuse to smuggle into gc. Cutting the pool into segment files turns “reclaim the coldest bytes” into remove_file: O(1), no rewrite, no read amplification, and a crash during it leaves a pool that is merely shorter.

§The recency argument, stated honestly

Segments are appended in first-read order, so dropping the oldest drops the objects least recently admitted. That is not the same as least recently used — an object read at minute 1 and again at minute 90 sits in an old segment. It is not promoted, and it does not need to be: when its segment is dropped, the next read of it misses, re-inflates its chain, and writes it back into the newest segment. Re-entry-on-miss is what makes this LRU-shaped, at the cost of one chain inflate per hot object per eviction cycle rather than a copy per hit. So it is FIFO over segments with re-entry-on-miss, and the thing an LRU is wanted for — a working set that stays resident under a scan of everything else — holds, because the working set is what keeps re-entering.

§What a full-history read now costs on disk

The plateau, not the climb. A reader that walks every object of a Linux-sized repository writes back every object it inflates; the pool grows to the budget and then stops, because each rotation past it drops the oldest segment. That is the number lane Q reports as its headline, and the_tier_plateaus_at_its_budget_under_a_full_scan is the test that asserts it rather than the sentence.

Re-exports§

pub use crate::arms::ENV_EXPLODE_BUDGET as ENV_BUDGET;

Structs§

CacheUse
What the tier is holding and what it is allowed to hold — the row a dashboard prints and Admin.StorageInfo carries.
ExplodedCache
The two pools, one door. Everything that used to call an ExplodedArchive calls this instead; the eager pool is an ExplodedArchive and behaves exactly as it did.
SuppressWriteBack
The guard suppress_write_back returns. See it for what this is for.

Constants§

BUDGET_CEILING
BUDGET_FRACTION_PERCENT
The default budget, and the arithmetic that makes it safe.
CACHE_FLUSH_BYTES
The lazy pool buffers this much before it writes. Far under crate::exploded_arrow::FLUSH_BYTES on purpose: that 64 MiB buffer is sized for an indexer draining a pack, and a reader’s write-back holding 64 MiB per store is memory the box did not agree to spend.
CHEAP_DEPTH
The cheapest thing a cache can do is not cache.
MAX_CACHED_BYTES
MIN_BUDGET

Functions§

budget_from_env
The budget a store opens with: the environment door if it names one, the filesystem-derived default otherwise.
default_budget
The default budget for a store rooted at dir, from that filesystem’s size.
default_global_line
The process-wide line for a store rooted at dir: the same tenth of the filesystem as default_budget, uncapped — the cap is per store, the tenth is per box. Every cache on one filesystem computes the same figure; caches on different filesystems each hold the sum to their own, which over-counts across mounts and is the conservative direction. 0 (no filesystem figure) turns the global check off rather than inventing one.
human
3.1G, 812M, 40K — one significant fraction, the same shape the dashboard’s other byte cells use.
parse_budget
12G, 512M, 1048576, 0, unlimited. An unparseable value is an ERROR and never a silent default — a typo in a quota is a bill that is wrong, and nobody notices until it is.
rebuild_cost
What it cost to produce an object, as the cache scores it.
set_budget_under
Set the budget of every open store under root (a prefix match, so an account’s whole tree is one call). Returns how many were changed — applied output, so a control plane that set nothing can say so.
suppress_write_back
Declare that reads on this thread are validation, not a reader’s.
use_all
Every open store’s explode band, as (store root, use). What the dashboard tick and Admin.StorageInfo read; a metadata call per pool and no store lock. Dead entries are swept on the way past.
worth_caching
Is this object worth a slice of the quota?
write_back_suppressed
Whether suppress_write_back is in force on this thread.