pub struct CacheConfig {Show 34 fields
pub cache_dir: Option<PathBuf>,
pub cache_size_mb: Option<u64>,
pub disk_headroom_mb: Option<u64>,
pub max_blob_size_mb: Option<u64>,
pub max_rate_per_mb: Option<u64>,
pub origin: Option<OriginConfig>,
pub origins: Option<Vec<OriginConfig>>,
pub pinned_hashes: Option<Vec<String>>,
pub origin_retry: Option<RetryPolicy>,
pub circuit_breaker: Option<CircuitBreakerPolicy>,
pub user_agent: Option<String>,
pub gc_interval_sec: Option<u64>,
pub fs_rescan_interval_sec: Option<u64>,
pub origin_probe_ttl_sec: Option<u64>,
pub origin_probe_negative_ttl_sec: Option<u64>,
pub origin_probe_fault_ttl_sec: Option<u64>,
pub origin_probe_timeout_ms: Option<u64>,
pub origin_probe_memo_capacity: Option<u64>,
pub eviction_high_water_pct: Option<u64>,
pub eviction_target_pct: Option<u64>,
pub eviction_per_sweep_budget: Option<u64>,
pub eviction_tick_secs: Option<u64>,
pub max_probe_holds: Option<u64>,
pub stake_lane_reserved_holds: Option<u64>,
pub node_to_node_pull_through_enabled: Option<bool>,
pub relay_foreign_namespaces: Option<bool>,
pub node_pull_probe_fanout: Option<usize>,
pub node_pull_timeout_sec: Option<u64>,
pub node_pull_stall_window_sec: Option<u64>,
pub node_pull_min_throughput_bps: Option<u64>,
pub eviction_policy: Option<String>,
pub admission_policy: Option<String>,
pub tinylfu: Option<TinyLfuConfig>,
pub serve_economics: Option<ServeEconomicsConfig>,
}Expand description
Cache section of the config file.
deny_unknown_fields is set so that a misplaced origin field (a flat
origin_url / origin_path / decompress directly under [cache])
gets a clear “unknown field” error at config load instead of a silent
“no origin configured” surprise at the first cache miss. Origin
settings live under the tagged [cache.origin] table — see
OriginConfig.
Fields§
§cache_dir: Option<PathBuf>Blob cache directory.
cache_size_mb: Option<u64>Maximum cache size in megabytes. An upper bound on the cache ceiling,
not the ceiling itself: the eviction driver clamps it down each tick so
the cache never grows into the last Self::disk_headroom_mb of free
space on the cache_dir volume (#1930). Set it deliberately large to let
the disk-headroom clamp size the cache to whatever the volume offers.
disk_headroom_mb: Option<u64>Free disk in megabytes the eviction driver keeps unused on the
cache_dir volume, defended against any process, not just this cache
(#1930). Absent => crate::config::DEFAULT_DISK_HEADROOM_MB (8192, 8
GiB). Each tick the effective ceiling is
footprint + max(0, free_disk - disk_headroom_mb), capped by
Self::cache_size_mb; below the headroom the driver evicts to claw disk
back. 0 opts out of the disk clamp (only cache_size_mb binds). A slow
soft-evict/GC cycle means the margin is defended reactively, so keep it
comfortably above one GC interval’s worth of writes.
max_blob_size_mb: Option<u64>Maximum single blob size in megabytes.
max_rate_per_mb: Option<u64>Buyer-side ABSOLUTE per-MB rate ceiling for paid pulls (#1375), in the same
per-MB units as the wire StreamResponse.rate_per_mb. Absent / 0 =
unlimited. Bounds what this node, as a BUYER on a cache-miss pull, will
accept a provider to quote — on top of the always-applied probe-relative
bound. This is a buyer-side ceiling; the node never bounds its own
sell rate (a rate below the on-chain delivery floor still sells and
settles, with only its vote-weight byte credit clamped at redemption).
origin: Option<OriginConfig>Single origin backend for cache pull-through (#437). Absent =>
no pull-through (unless Self::origins is set); cache misses
fail with NoOrigin. The variant (http, fs, or s3) is
selected by the kind field on the inner [cache.origin]
table. Mutually exclusive with Self::origins — operators
who need an ordered fallback list use the [[cache.origins]]
array-of-tables form instead. Setting both at once is rejected
at config resolution.
origins: Option<Vec<OriginConfig>>Ordered list of origin backends with automatic fallback (#284).
On a cache miss the engine tries each entry in order; the next
entry is consulted when the current one returns NotFound,
returns a permanent error, or exhausts its per-origin transient
retry budget. Deterministic per-origin failures (hash mismatch,
blob-too-large) do not trigger fallback — they indicate a
misbehaving or misconfigured backend that must surface, not be
masked. Mutually exclusive with Self::origin; an empty
array is rejected (omit the key for “no pull-through” rather
than configuring zero origins). Duplicate entries are
permitted but logged as a warning at startup — operators may
legitimately want two entries with the same target for
connection-pool sharding. Set once at startup; changes require
a process restart, consistent with Self::origin_retry.
Worst-case backoff latency for an all-failing chain is
len(origins) × sum_of_backoffs(origin_retry) plus per-attempt
origin RTT/timeout. With default decdn_config_types::RetryPolicy
(3 retries, sleeps 100 + 200 + 400 ms ± jitter — see
RetryPolicy::default) that’s ~700 ms backoff per origin
(~2.1 s for three origins) — not max_retries × backoff_cap,
because the default schedule never saturates the 10 s cap. The
max_backoff_ms cap only dominates if initial_backoff_ms is
raised enough to saturate it; for the worst-case-cap regime use
max_retries × max_backoff_ms and multiply by chain length.
Plan operator timeouts accordingly — RTT/timeout per attempt
usually dominates the backoff term.
pinned_hashes: Option<Vec<String>>Hex-encoded BLAKE3 hashes that must stay cached regardless of LRU pressure (#276). Each entry is 64 lowercase hex chars (BLAKE3 digest size). Invalid hex or wrong-length entries cause config resolution to fail — fail-fast at load time beats a silent “entry was ignored” surprise hours later when the operator discovers the blob got evicted anyway.
origin_retry: Option<RetryPolicy>Origin pull-through retry policy (#285, extended in #519).
Controls exponential backoff for transient HTTP / S3 /
filesystem errors and the body-phase retry strategy for
mid-stream io::Errors. Absent => defaults from
decdn_config_types::RetryPolicy::default (3 retries, 100ms
initial backoff doubling to 10s cap, 10% jitter,
buffered_max_bytes = 4 MiB). max_retries = 0 opts out and
makes one attempt with no retry; buffered_max_bytes = 0
independently disables the body-phase buffer path so all
body-phase failures route through the streaming abort+restart
path. Set once at startup; changes require a restart.
Body-phase retry trade-offs (#519):
- Buffer-then-commit applies when the origin’s advertised
size_hintis at or belowbuffered_max_bytes. The body drains into memory before the iroh-blobs commit, so failed attempts cost RAM (bounded bybuffered_max_bytesper in-flight fetch) but leave no on-disk garbage. - Abort + restart applies above the threshold or when
size_hintis unknown. Each failed attempt strands up tomax_blob_size_mbof partial-import bytes until iroh-blobs GC reclaims them atcache.gc_interval_seccadence. Worst-case disk amplification per fetch is(1 + max_retries) * max_blob_size_mb.
decdn_config_types::RetryPolicy carries #[serde(default)] so
partial sections (e.g. just max_retries = 5) get the rest of
the fields filled from defaults.
circuit_breaker: Option<CircuitBreakerPolicy>Per-origin circuit-breaker policy (#963). Fronts each origin’s
pull-through retry loop: after failure_threshold consecutive
origin-unavailable failures (transient errors that exhausted the
retry budget — NOT 404s or other permanent per-object errors) the
breaker trips OPEN and fast-fails every miss for cooldown_ms
without incurring any retry/backoff, then admits
half_open_max_calls trial pulls to probe recovery. Absent =>
defaults from decdn_config_types::CircuitBreakerPolicy::default
(enabled, trip after 5 failures, 30s cooldown, 1 half-open trial).
Set enabled = false (or failure_threshold = 0) to opt out and
run every miss through the full retry loop regardless of origin
health. Set once at startup; changes require a restart.
CircuitBreakerPolicy carries #[serde(default)] so partial
sections (e.g. just cooldown_ms = 60000) get the rest of the
fields filled from defaults.
user_agent: Option<String>Optional User-Agent override sent on every HTTP origin
pull-through request (#435). Absent => the workspace default
(decdn-node/<version>); set to attribute CDN traffic in origin
access logs or to apply origin-side rate limits and routing
policy that distinguish CDN pulls from end-user clients. Empty
string is rejected at resolution.
gc_interval_sec: Option<u64>Interval between iroh-blobs GC sweeps in seconds (#518). Absent =>
crate::config::DEFAULT_GC_INTERVAL_SEC. Set to 0 to disable
the periodic sweep — operators with external disk-reclaim
scheduling can opt out, accepting that bytes orphaned by
hash-mismatch / mid-stream-error pull-through paths leak until
they manually reset state. Tighter intervals shrink the
hostile-origin amplification window at the cost of more
list+sweep CPU per minute.
fs_rescan_interval_sec: Option<u64>Interval between origin-held-index rescans in seconds (#1130). Absent =>
crate::config::DEFAULT_FS_RESCAN_INTERVAL_SEC. The node re-walks its
fs origin directory (and re-checks present pins) at this cadence so a
file dropped into the origin becomes discoverable — probe has_blob +
DHT announce — within one interval, without a restart or reload. 0
disables the periodic rescan (startup + decdn node reload still run
it). Shorter intervals pick up new files faster at the cost of more
directory-walk + size() I/O per minute.
origin_probe_ttl_sec: Option<u64>TTL, in seconds, for a memoised positive live-origin probe answer (#1130
pt3). Absent => crate::config::DEFAULT_ORIGIN_PROBE_TTL_SEC (15). A
probe for a hash absent from the fs-enumeration ∪ pins index falls back
to a live HEAD/HeadObject against the http/s3 origin; a present-with-
size answer is cached for this long so a non-pinned bucket object is
discoverable without a per-probe origin round-trip on every probe.
Shorter TTLs track origin deletions faster at the cost of more HEAD
traffic. See origin_probe_negative_ttl_sec for the absent-answer TTL.
origin_probe_negative_ttl_sec: Option<u64>TTL, in seconds, for a memoised negative (absent) live-origin probe
answer (#1130 pt3). Absent =>
crate::config::DEFAULT_ORIGIN_PROBE_NEGATIVE_TTL_SEC (2). Caching the
negative answer is what blunts a random-hash probe flood; keeping this
TTL short bounds how long a stale Absent can hide newly-available own
content from a probe.
origin_probe_fault_ttl_sec: Option<u64>TTL, in seconds, for a memoised fault live-origin probe answer (#1130
pt3). Absent => crate::config::DEFAULT_ORIGIN_PROBE_FAULT_TTL_SEC
(5). Longer than origin_probe_negative_ttl_sec because the memo is
keyed per hash and a client retrying one hash against a failing origin
is the common shape; shorter than origin_probe_ttl_sec because a
memoised fault refuses paying clients for as long as it stands.
Rejected unless origin_probe_negative_ttl_sec <= origin_probe_fault_ttl_sec <= origin_probe_ttl_sec.
origin_probe_timeout_ms: Option<u64>Per-probe ceiling, in milliseconds, on the live-origin HEAD/HeadObject
(#1130 pt3). Absent => crate::config::DEFAULT_ORIGIN_PROBE_TIMEOUT_MS
(2000). A slow origin must never stall the probe hot path; on timeout the
probe answers has_blob: false (safe — never slashable) and
the miss is memoised as absent for one TTL.
origin_probe_memo_capacity: Option<u64>Maximum distinct hashes held in the live-origin probe memo (#1130 pt3).
Absent => crate::config::DEFAULT_ORIGIN_PROBE_MEMO_CAPACITY (4096).
Bounds memo memory under a random-hash probe flood; the cache is
best-effort, so at capacity one arbitrary entry is dropped to admit a new
answer (expired entries reclaimed first).
eviction_high_water_pct: Option<u64>Cache eviction driver: percent of Self::cache_size_mb above which
the driver actively evicts (#1173, ADR 040). Absent =>
crate::config::DEFAULT_EVICTION_HIGH_WATER_PCT (90). Hard bounds
[60, 95]; set above the 25% probe-hold recommendation so a full hold
budget plus in-flight writes don’t trip it, below 95% for write
headroom between sweeps.
eviction_target_pct: Option<u64>LRU eviction driver: percent of Self::cache_size_mb the driver
evicts down to before returning to idle (#1173). Absent =>
crate::config::DEFAULT_EVICTION_TARGET_PCT (80). Hard bounds
[40, 90], and MUST be <= eviction_high_water_pct - 5 — the 5-point
hysteresis gap is structural (prevents thrash on writes hovering near
the trigger), not a tunable nicety.
eviction_per_sweep_budget: Option<u64>LRU eviction driver: maximum candidates removed per tick before the
driver yields (#1173). Absent =>
crate::config::DEFAULT_EVICTION_PER_SWEEP_BUDGET (16). Hard bounds
[1, 256]. Bounds worst-case driver-induced latency on the cache hot
path (~1 ms filesystem-unlink per entry); the driver continues across
consecutive ticks until below target or candidates are exhausted.
eviction_tick_secs: Option<u64>LRU eviction driver: seconds between driver wakeups (#1173). Absent =>
crate::config::DEFAULT_EVICTION_TICK_SECS (1). Hard bounds
[1, 60].
Cost note: every tick measures the on-disk footprint, which walks the
blob list and issues one status() per blob, plus an O(n) clone of the
pinned set — latched or not. An idle tick is not free: the driver keeps
no cached footprint, so it re-measures on every tick (ADR 040). On a
cache holding many blobs,
raise this to trade eviction latency for steady-state store load.
max_probe_holds: Option<u64>Maximum number of concurrently held (eviction-exempt) blobs for the
probe-triggered hold (ADR 005 §Hold budget, #318). Holds are
per-blob: multiple peers probing the same hash share one slot. When the
budget is exhausted, a probe for a blob that is present but gets no slot
still advertises (has_blob: true) and places no eviction hold — the
blob may be LRU-evicted before the pull, costing one wasted round trip
(never a slash). Absent => crate::config::DEFAULT_MAX_PROBE_HOLDS
(256). 0 is the operator opt-out: holds are disabled and probes answer
has_blob: false for store-backed content — content servable from a
configured origin takes no hold and is still advertised. Operators with
small caches SHOULD set this to ≤25% of cache capacity.
stake_lane_reserved_holds: Option<u64>Number of probe-hold slots reserved for the stake lane —
registered operators issuing node-to-node cache-miss probes (#757,
ADR 003 §Admission and Priority). An end-client probe places no eviction
hold once usage reaches max_probe_holds - stake_lane_reserved_holds (it
still advertises has_blob: true if the blob is present), keeping the
last stake_lane_reserved_holds slots available for node-to-node probes
so end-client load cannot starve their holds. Absent / 0 (the default)
disables the reservation entirely — a single-lane node is unaffected.
Values >= max_probe_holds reserve the whole budget for the stake lane:
no end-client probe takes a hold slot, regardless of current usage.
node_to_node_pull_through_enabled: Option<bool>Enable node-to-node paid cache-miss pull-through (#831, ADR 001/022).
Absent / false (the default) → a cache miss serves NotFound as
before. When true and the buyer-channel service bootstrapped, a
miss triggers DHT provider discovery → probe → ranked paid pull from an
upstream node, which populates the cache and is then served. OFF by
default for the initial network: enabling it makes the node front USDC
egress to fill misses (bounded by blockchain.buyer_working_deposit_micro_usdc
and the upstream’s per-MB rate), and the serving path only triggers it
behind a valid, pool-bound client so an unpaid request cannot drive
egress.
relay_foreign_namespaces: Option<bool>When false, the node serves and seeds only content its own backend
holds; a foreign hash is declined like a miss. Absent resolves to a
role-derived default: false (origin-only) when an origin backend is
configured, true (relay) when none is. Set this explicitly to
override either default. Node-local; cache.* is restart-required.
node_pull_probe_fanout: Option<usize>Number of discovered providers to probe before ranking on a
node-to-node pull (#831). Absent =>
crate::config::DEFAULT_NODE_PULL_PROBE_FANOUT (5). Higher widens
provider choice at the cost of more probe round trips per miss; 0
probes none, so no pull can succeed (a way to disable the pull while
keeping the feature flag on).
node_pull_timeout_sec: Option<u64>Wall-clock timeout in seconds for the STREAM-OPEN stage of a single upstream pull
on a node-to-node miss (#831) — connect, handshake, and the
signed StreamResponse. It does NOT bound the buyer-channel open, which precedes
it on its own 5 s budget. Absent =>
crate::config::DEFAULT_NODE_PULL_TIMEOUT_SEC (20). Bounds how long a
miss blocks the serving path on one upstream before falling through to
the next ranked candidate (or NotFound). This is the per-upstream
budget; the overall pull-through deadline is derived as roughly
MAX_PROVIDER_ATTEMPTS × (channel open + it + stall) plus a fixed discovery
allowance, so the fallback loop reaches every ranked candidate (#859).
It does NOT bound the streaming stage (#1134) — that is
Self::node_pull_stall_window_sec. A wall clock over the bytes would
cap the blob size a node can pull through at roughly
this × link speed.
node_pull_stall_window_sec: Option<u64>Throughput-floor window in seconds for the STREAMING stage of an upstream pull
(#1797). Absent => crate::config::DEFAULT_NODE_PULL_STALL_WINDOW_SEC (20). Bytes
read off the QUIC stream are counted sub-frame, and the pull aborts when the bytes
across the trailing window fall below Self::node_pull_min_throughput_bps — so the
signal is frame-size-independent and indifferent to blob size. It is what makes a pull
of any size safe to leave uncapped.
Tune it against upstream responsiveness, not content size: too short and a brief network hiccup abandons a healthy transfer; too long and a dead upstream is held onto for longer than necessary before the fallback loop moves on. The abort is requester-local policy and does NOT score the upstream’s reputation (#1797).
“Longer” is multiplied, not added. A silent candidate costs one full window of this, and the derived outer deadline budgets that for EVERY candidate, so a second here is ~3 seconds of worst-case client wait on a total miss (167.5 s at defaults).
It also sets the head start of the time budget for each own-origin range-pull read
(ADR 037): one {H}.obao4 fetch or one data window of at most 4 MiB. That budget is
this window plus the read size at Self::node_pull_min_throughput_bps, so it
scales with the read and never caps the blob size.
node_pull_min_throughput_bps: Option<u64>Minimum sustained upstream throughput in bytes per second over
Self::node_pull_stall_window_sec (#1797). Absent =>
crate::config::DEFAULT_NODE_PULL_MIN_THROUGHPUT_BPS (4096). A stream whose
throughput stays below this floor for a full window is abandoned — catching both a
wedged upstream (throughput to zero) and a slow drip (a trickle that never trips a
bare idle timeout). 0 disables the throughput test and leaves pure idle detection:
at least one byte per window.
It also sets the average throughput each own-origin range-pull read must sustain
after the Self::node_pull_stall_window_sec head start (ADR 037). A read past that
budget fails as an origin fault and bumps decdn_cache_origin_range_timeouts_total.
Such a read returns the whole body at once, so it has no idle form: 0 leaves
those reads unbounded.
eviction_policy: Option<String>Cache eviction policy selector (ADR 040). Absent =>
crate::config::DEFAULT_EVICTION_POLICY ("lru"). Must be "lru" or
"tinylfu"; an unrecognized name is rejected at config load rather than
silently falling back.
admission_policy: Option<String>Cache admission policy selector (ADR 040). Absent =>
crate::config::DEFAULT_ADMISSION_POLICY ("always"). Must be
"always" or "tinylfu"; an unrecognized name is rejected at config
load rather than silently falling back.
tinylfu: Option<TinyLfuConfig>W-TinyLFU tuning knobs (ADR 040). Consulted whenever
Self::eviction_policy or Self::admission_policy is "tinylfu";
otherwise unused. Absent => every field defaults.
serve_economics: Option<ServeEconomicsConfig>Refuse-to-serve economics tuning (ADR 041): the margin policy, its discount, and per-source speculative-warming allowance. Absent => every field defaults.
Trait Implementations§
Source§impl Debug for CacheConfig
impl Debug for CacheConfig
Source§impl Default for CacheConfig
impl Default for CacheConfig
Source§impl<'de> Deserialize<'de> for CacheConfig
impl<'de> Deserialize<'de> for CacheConfig
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Auto Trait Implementations§
impl Freeze for CacheConfig
impl RefUnwindSafe for CacheConfig
impl Send for CacheConfig
impl Sync for CacheConfig
impl Unpin for CacheConfig
impl UnsafeUnpin for CacheConfig
impl UnwindSafe for CacheConfig
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<T> DeserializeOwned for Twhere
T: for<'de> Deserialize<'de>,
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more