Skip to main content

Module eviction

Module eviction 

Source
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§

EvictionParams
Resolved driver parameters. Every field is range-checked at config resolution (resolve_cache_into): high_water_pct ∈ [60, 95], target_pct ∈ [40, 90] and target_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 shutdown fires. Intended to be tasks.spawn(run(...))-ed into the runtime JoinSet; returns when the oneshot is dropped or sent.