Use case · Cadence
Tick a grid-aligned publication cadence
interval(clock, period) ticks on a fixed grid and returns the scheduled stamp, not the observed time. Publication cadences and heartbeats replay exactly, and a consumer stall skips missed ticks rather than bursting a catch-up.
A publication loop
Build a ClockInterval and drive it from your publish loop. The first tick lands at now + period; each tick() returns the grid-aligned scheduled timestamp.
use std::time::Duration;
use ferro_replay::interval;
let mut publish = interval(clock.clone(), Duration::from_secs(1));
loop {
let tick_ns = publish.tick().await;
// tick_ns is the scheduled grid stamp — replay reproduces it
// exactly, so the published snapshot timestamps are deterministic.
publish_snapshot(tick_ns);
}tick() returns the deadline it was scheduled for, not the wall-clock moment it fired. That is what lets a 1-second publication cadence or a websocket heartbeat ts replay byte-for-byte.
Skip-not-burst
If a consumer stalls, the already-armed deadline fires late once — a prompt catch-up publication — then the schedule realigns to the first grid point strictly after now. Missed ticks in between are skipped, never bursted. This matches tokio's MissedTickBehavior::Skip.
// period = 1s. Consumer takes the 1s tick, then stalls until 4.5s.
assert_eq!(iv.tick().await, 1_000_000_000);
// The armed 2s deadline fires late, once...
assert_eq!(iv.tick().await, 2_000_000_000);
// ...and 3s/4s are skipped — the schedule realigns to 5s, the first
// grid point past 4.5s. No thundering catch-up.
assert_eq!(iv.tick().await, 5_000_000_000);During realignment a tick due exactly at nowis skipped rather than fired. This is a deterministic, documented divergence from tokio and is immaterial at the engine's 250ms–15s cadences.
Cancel-safety
tick() is cancel-safe: the deadline is not consumed until the sleep completes, so a cancelled select! arm re-arms the same deadline and the next tick() still fires on the grid.