Expand description
Cache-eviction driver loop (#1173, ADR 040 §Whole-blob reclaim; range eviction is upstream-gated).
A single async task, owned by the node wiring layer (per
appendix-poc-production-seams.md), that enforces the cache ceiling the
write path deliberately does not. It periodically measures on-disk footprint
and, once over the high-water mark, releases the blobs the injected
EvictionPolicy ranks for eviction (via
CacheEngine::release_for_eviction, the soft-evict path that is not the
durable operator-evict) down to the target mark, bounded by a per-sweep
budget.
§The ceiling is disk-aware
The ceiling is not the static cache.cache_size_mb. That is an upper bound;
each tick the driver probes free disk on the cache_dir volume and clamps
the ceiling down to keep cache.disk_headroom_mb free, defended against any
process (#1930). See effective_cap. So a large cache_size_mb lets a node
size its cache to whatever disk the machine offers, and the high-water and
target marks track that dynamic ceiling.
§The driver’s actuator is indirect — hence pending-reclaim accounting
release_for_eviction does not free disk. It drops the blob’s
GC-protecting tag; the bytes are reclaimed later by the independent
iroh-blobs GC sweep (cache.gc_interval_sec, default 300s). The driver ticks
far more often than that (eviction_tick_secs, default 1s), so a naive loop
that re-measured raw disk each tick would see its own releases have no effect
and keep releasing — roughly gc_interval_sec / eviction_tick_secs sweeps’
worth (~300 × 16 ≈ 4800 blobs at defaults) for a pressure event needing a
handful. That would drain the cache on a single high-water crossing.
So the driver tracks pending_reclaim: bytes it has released but not yet
seen reclaimed. Decisions are made against the effective footprint
(raw − pending_reclaim) — what disk will look like once GC lands — while
the decdn_cache_bytes gauge still reports honest raw usage. When a
measurement comes in below the previous one, GC has run, and the observed
drop is credited against pending_reclaim. The accounting is self-correcting:
it needs no coupling to the GC schedule and recovers if a release is never
reclaimed.
With GC disabled (gc_interval_sec == 0) nothing is ever reclaimed, so the
driver releases roughly one target’s worth once and then idles rather than
stripping every tag in the cache. The runtime warns at startup in that case —
the driver cannot reclaim space and the ceiling is unenforceable.
§Hysteresis
A latch: the driver starts evicting when the effective footprint crosses
eviction_high_water_pct and does not stop until it reaches
eviction_target_pct, then idles until the next high-water crossing. The
≥5-point gap between the two (enforced at config resolution) is what prevents
thrash on writes hovering near the trigger.
Structs§
- Eviction
Params - Resolved driver parameters. Every field is range-checked at config
resolution (
resolve_cache_into):high_water_pct ∈ [60, 95],target_pct ∈ [40, 90]andtarget_pct <= high_water_pct - 5(the structural hysteresis gap),per_sweep_budget ∈ [1, 256],tick ∈ [1s, 60s]. The type itself carries no invariant — it is a wiring DTO with a single production construction site.
Functions§
- run
- Run the eviction driver until
shutdownfires. Intended to betasks.spawn(run(...))-ed into the runtimeJoinSet; returns when the oneshot is dropped or sent.