Expand description
DHT republish scheduler (ADR 022 §STORE Flow & §Bootstrap).
When a hash becomes advertisable — a whole-blob commit, or a ranged
admit that completes a discovery block
(decdn_cache::CacheEngine::subscribe_inserts) — and has not announced
yet, the scheduler eagerly publishes it to its K+3 closest peers with
per-hash StoreRequests (a size-1 batch buys nothing) and schedules its
next republish. A hash that has announced waits for its cycle, which
carries its widened coverage.
On a periodic per-record jittered timer the scheduler:
- Drains every record whose republish window has come due. On a
tick where one has, it also drains the records due within
REPUBLISH_LOOKAHEAD, in due order, while no receiver’s set passesLOOKAHEAD_RECEIVER_CAP(ADR 022 §STORE Flow “Drain cycle”). It keeps those still held in the cache. Per-record jitter spreads due times so thinly that a 1 s tick alone holds about one hash; the look-ahead is what gives a steady-state batch more than one. - Groups the drained hashes by receiver — each hash’s K+3 closest
peers — so all hashes bound for one receiver ride a single
BatchStoreRequest { hashes, holder: self }, split at theMAX_BATCH_STORE_HASHESwire cap. This is one RPC per publisher-receiver pair per cycle (ADR 022 §STORE Flow Batched STORE & §DHT Bandwidth Analysis). Every DHT node implementsBatchStore, so there is no per-hash fallback. - Schedules the next republish at
now + uniform(30 min, 50 min)per ADR 022 §STORE Flow step 3. Jitter is drawn independently per record so the next republish window for a given hash is not predictable from outside the publisher. Taking a record early only shortens its refresh gap: the draw keeps it at or below 50 min, andREPUBLISH_LOOKAHEADkeeps it at or above 25 min.
Cold-start (ADR 022 §Bootstrap “Cold-start re-publish scheduling”):
a node booting with C cached blobs draws the first republish offset
from uniform(0, 40 min) per blob, NOT uniform(30, 50). The
single-tick “republish everything on the next scheduler tick”
pattern is explicitly non-conforming — it saturates the per-peer
rate limit at every receiver. The cold-start uniform-0-40-min
window matches the steady-state mean cycle so bootstrap rate equals
steady-state rate by construction.
Lag sweep (ADR 022 §Bootstrap “Re-seed after a lost commit
window”): the commit-event channel is bounded and best-effort, and
the cache retains nothing it drops, so a Lagged receiver cannot
backfill. The scheduler instead re-derives the held set
(holder_snapshot) and seeds whatever is missing. Seeding is
idempotent and uses the same uniform(0, 40 min) window as cold
start, so a sweep costs one cold-start window rather than a burst —
the immediate bulk republish is non-conforming here for the same
reason it is at boot.
Implementation: a single tokio task drives all records via a
BinaryHeap<(due_at, hash)> and a 1 s ticker. The heap is small (one
entry per cached blob, capped by the cache’s blob count). Sending does
not hold the heap mutex; a drain cycle’s batches go out in a spawned
task so a slow receiver doesn’t stall the rest of the schedule.
Structs§
- Republish
Scheduler - Republish-scheduler handle. Owns the per-hash heap and the abort signal so the runtime can stop it on shutdown.
Constants§
- COLD_
START_ MAX - Cold-start jitter window upper bound (ADR 022 §Bootstrap “Cold- start re-publish scheduling”). The lower bound is 0 — a record drawn at 0 fires on the next tick.
- LOOKAHEAD_
MIN_ SPACING - The shortest time between a drain cycle and a later cycle that pulls records forward (ADR 022 §STORE Flow “Drain cycle”). The per-receiver cap bounds one cycle, not two a second apart: after a backlog empties a receiver’s per-peer bucket, a look-ahead batch on the next tick would find about 20 tokens and have its tail refused. At the 20-token/s per-peer refill, 2 s refill the whole 40-token burst.
- LOOKAHEAD_
RECEIVER_ CAP - The most hashes one receiver’s set holds once the look-ahead has pulled
records forward (ADR 022 §STORE Flow “Drain cycle”). A full batch fits the
40-token per-peer burst of ADR 022 §DHT Rate Limiting with 8 tokens to
spare for the same publisher’s lookups and eager
Stores. Records already due are not held to it. - REPUBLISH_
FANOUT - Number of receivers per republish (ADR 022 §STORE Flow step 1:
“K+3 closest nodes to H”). Three slots beyond
K=20give the record headroom against an attacker who suppresses up to K receivers. - REPUBLISH_
LOOKAHEAD - How far ahead a drain cycle reaches for records not yet due (ADR 022
§STORE Flow “Drain cycle”). Sending early only shortens a record’s refresh
gap, which the
STEADY_STATE_MAXdraw bound keeps inside the receiver-anchored 1 h TTL. This bound keeps the gap at or above 25 min, which caps the extra refresh load the look-ahead adds. - STEADY_
STATE_ MAX - Steady-state maximum republish gap. ADR 022 §STORE Flow step 3.
- STEADY_
STATE_ MIN - Steady-state minimum republish gap. ADR 022 §STORE Flow step 3.
Functions§
- run_
republish - Long-running task: consumes a
broadcast::Receiver<Hash>from the cache, drives the scheduler heap, and publishes each hash that has not announced yet with aStoreto its K+3 closest peers and each drain cycle with oneBatchStoreper receiver. Exits onstop, or on a closed cache subscribe channel — which this task’s ownCacheEngineclone makes unreachable in practice.