Skip to main content

CacheConfig

Struct CacheConfig 

Source
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_hint is at or below buffered_max_bytes. The body drains into memory before the iroh-blobs commit, so failed attempts cost RAM (bounded by buffered_max_bytes per in-flight fetch) but leave no on-disk garbage.
  • Abort + restart applies above the threshold or when size_hint is unknown. Each failed attempt strands up to max_blob_size_mb of partial-import bytes until iroh-blobs GC reclaims them at cache.gc_interval_sec cadence. 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

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for CacheConfig

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl<'de> Deserialize<'de> for CacheConfig

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl Serialize for CacheConfig

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ

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

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
where F: FnOnce(&Self) -> bool,

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

impl<T> MaybeSend for T
where T: Send,

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<'de, T> RpcBorrow<'de> for T
where T: Deserialize<'de> + Debug + Send + Sync + Unpin,

Source§

impl<T> RpcRecv for T
where T: DeserializeOwned + Debug + Send + Sync + Unpin + 'static,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more