Skip to main content

Module publish

Module publish 

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

  1. 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 passes LOOKAHEAD_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.
  2. 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 the MAX_BATCH_STORE_HASHES wire cap. This is one RPC per publisher-receiver pair per cycle (ADR 022 §STORE Flow Batched STORE & §DHT Bandwidth Analysis). Every DHT node implements BatchStore, so there is no per-hash fallback.
  3. 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, and REPUBLISH_LOOKAHEAD keeps 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§

RepublishScheduler
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=20 give 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_MAX draw 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 a Store to its K+3 closest peers and each drain cycle with one BatchStore per receiver. Exits on stop, or on a closed cache subscribe channel — which this task’s own CacheEngine clone makes unreachable in practice.