Deterministic hashed wheel timer with keyed deduplication.
HashedWheelTimer<T>: core single-level hashed wheel timer.KeyedHashedWheelTimer<T, K, F>: keyed dedup wrapper over the core timer.- Stable pop ordering by
(tick, delta_tick, event_id).
[dependencies]
okee-wheel-timer = "0.1.0"use okee_wheel_timer::HashedWheelTimer;
// 8 buckets in one wheel cycle.
let mut wheel = HashedWheelTimer::new(8);new(8)means one wheel cycle has 8 buckets.- Time is tracked by
tick. step()advances one tick and moves to the next bucket.- Events are stored with
(tick, delta_tick).tickis the absolute time when the event is eligible.delta_tickis the wave index inside the same tick.
delta_tick prevents processing newly scheduled "same tick" events in the same wave.
use okee_wheel_timer::HashedWheelTimer;
let mut wheel = HashedWheelTimer::new(8);
let created = wheel.schedule(0, "payload");
let first_wave = wheel.pop_events();
assert_eq!(first_wave.len(), 1);
assert_eq!(first_wave[0].id(), created.id);
assert_eq!(first_wave[0].tick(), 0);
assert_eq!(first_wave[0].data(), &"payload");HashedWheelTimer always creates a new event ID. KeyedHashedWheelTimer can replace an existing event by your dedup key.
use okee_wheel_timer::KeyedHashedWheelTimer;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct Job {
user_id: u64,
queue: u8,
payload: i32,
}
let mut wheel = KeyedHashedWheelTimer::new(8, |job: &Job| (job.user_id, job.queue));
let first = wheel.schedule(
10,
Job {
user_id: 42,
queue: 1,
payload: 100,
},
);
let second = wheel.schedule(
12,
Job {
user_id: 42, // same user_id and queue as previous
queue: 1,
payload: 200,
},
);
assert_eq!(second.replaced_id, Some(first.id));
assert!(!wheel.contains_by_id(first.id));
assert!(wheel.contains_by_id(second.id));- In keyed mode, you are responsible for dedup semantics.
- The dedup key is produced from
&T(Fn(&T) -> K). - If timing should affect uniqueness, include timing fields in your payload
Tand key them explicitly.
- Call
pop_events()whilehas_events_in_current_tick()istrue. - Call
step()to move to the next tick. - Typical loop:
while wheel.has_events_in_current_tick() {
let events = wheel.pop_events();
// process events
}
wheel.step();Benchmarks are implemented with Criterion in benches/wheel.rs.
# run all timer benchmarks
cargo bench --bench wheel
# save current results as baseline
cargo bench --bench wheel -- --save-baseline v1
# compare current run with a saved baseline
cargo bench --bench wheel -- --baseline v1HTML reports are generated in target/criterion/report/index.html.