Kestrel Timer
High-performance async timer library based on Hierarchical Timing Wheel algorithm
π Table of Contents
- Overview
- Key Features
- Quick Start
- Architecture
- Usage Examples
- Configuration
- Benchmarks
- Use Cases
- License
Overview
kestrel-timer is a high-performance async timer library based on the Hierarchical Timing Wheel algorithm, designed for Rust and Tokio runtime. It provides O(1) time complexity for timer operations and easily handles 10,000+ concurrent timers.
Core Advantages:
- Hierarchical timing wheel architecture that automatically separates short-delay and long-delay tasks
- 2-12x performance improvement over traditional heap-based implementations
- Support for timer postponement, batch operations, and completion notifications
- Production-ready with comprehensive testing
Key Features
ποΈ Hierarchical Timing Wheel
- Dual-layer design: L0 layer (high-precision short-delay) + L1 layer (long-delay) with automatic layering
- Smart demotion: L1 tasks automatically demote to L0 layer when due
- No round checks: L0 layer eliminates rounds checking for 90% of tasks
β‘ High Performance
- O(1) time complexity: Insert, delete, and trigger operations are all O(1)
- Optimized data structures:
FxHashMap+parking_lot::Mutex - Bitwise optimization: Slot count is power of 2 for fast modulo operations
- Large-scale support: Easily handles 10,000+ concurrent timers
π Complete Features
- β Async callback support (based on Tokio)
- β Timer postponement (keep or replace callback)
- β Batch operations (schedule, cancel, postpone)
- β Completion notification mechanism
- β TimerService (Actor-based management)
- β Thread-safe
Quick Start
Installation
Add to your Cargo.toml:
[]
= "0.1.0"
= { = "1.48", = ["full"] }
Basic Usage
use ;
use Duration;
async
Batch Operations
use CallbackWrapper;
let timer = with_defaults;
// Batch create + register
let callbacks: =
.map
.collect;
let tasks = create_batch_with_callbacks;
let batch_handle = timer.register_batch;
// Batch cancel
batch_handle.cancel_all;
Postpone Timer
let callback = Some;
let task = create_task;
let task_id = task.get_id;
let handle = timer.register;
// Postpone and keep original callback
timer.postpone;
// Postpone and replace callback
let new_callback = Some;
timer.postpone;
TimerService Usage
use ;
let timer = with_defaults;
let mut service = timer.create_service;
// Batch schedule
let callbacks = vec!;
let tasks = create_batch_with_callbacks;
service.register_batch.unwrap;
// Receive timeout notifications
let mut timeout_rx = service.take_receiver.unwrap;
while let Some = timeout_rx.recv.await
service.shutdown.await;
Architecture
Hierarchical Timing Wheel Design
βββββββββββββββββββββββββββββββββββββββββββββββ
β L1 Layer (Upper) β
β Slots: 64 | Tick: 1s | Range: 64s β
β β Demote to L0 β
βββββββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββββββββ
β L0 Layer (Lower) β
β Slots: 512 | Tick: 10ms | Range: 5.12s β
β β² Current Pointer β
βββββββββββββββββββββββββββββββββββββββββββββββ
L0 Layer (Lower - High Precision):
- Slots: 512 (default), Tick: 10ms
- Coverage: 5.12 seconds
- Handles 80-90% of short-delay tasks
L1 Layer (Upper - Long Duration):
- Slots: 64 (default), Tick: 1000ms
- Coverage: 64 seconds
- Handles long-delay tasks with rounds mechanism
Workflow:
- Short delay (< 5.12s) β Insert directly into L0 layer
- Long delay (β₯ 5.12s) β Insert into L1 layer
- L1 task due β Automatically demote to L0 layer
- L0 task due β Trigger immediately
Performance Optimizations
- Hierarchical architecture: Avoids single-layer round checks, L0 layer requires no rounds checking
- Efficient locking:
parking_lot::Mutexis faster than standard Mutex - Bitwise optimization: Slot count is power of 2, uses
& (n-1)for fast modulo - Cache optimization: Pre-compute slot masks, tick durations, and other frequently used values
- Batch optimization: Reduce lock contention with smart small-batch handling
Usage Examples
Full API documentation at docs.rs/kestrel-timer
Main APIs
TimerWheel:
TimerWheel::with_defaults()- Create with default configTimerWheel::new(config)- Create with custom configcreate_task(delay, callback)- Create task (static method)register(task)- Register taskregister_batch(tasks)- Batch registerpostpone(task_id, delay, callback)- Postpone taskpostpone_batch(updates)- Batch postpone
TimerHandle:
cancel()- Cancel timertask_id()- Get task IDinto_completion_receiver()- Get completion notification
TimerService:
create_task(delay, callback)- Create task (static method)register(task)- Register tasktake_receiver()- Get timeout notification receivercancel_task(task_id)- Cancel taskpostpone(task_id, delay, callback)- Postpone taskshutdown()- Shutdown service
Configuration
Default Configuration
let timer = with_defaults;
// L0: 512 slots Γ 10ms = 5.12 seconds
// L1: 64 slots Γ 1s = 64 seconds
Custom Configuration
use WheelConfig;
let config = builder
.l0_tick_duration // L0 tick
.l0_slot_count // L0 slots (must be power of 2)
.l1_tick_duration // L1 tick
.l1_slot_count // L1 slots (must be power of 2)
.build?;
let timer = new;
Recommended Configurations
High Precision (Network Timeouts):
let config = builder
.l0_tick_duration
.l0_slot_count
.l1_tick_duration
.l1_slot_count
.build?;
Low Precision (Heartbeat Detection):
let config = builder
.l0_tick_duration
.l0_slot_count
.l1_tick_duration
.l1_slot_count
.build?;
Benchmarks
Run Benchmarks
Performance Comparison
Compared to traditional heap-based (BinaryHeap) timer implementations:
| Operation | Hierarchical Wheel | Heap Implementation | Advantage |
|---|---|---|---|
| Insert Single | O(1) ~5ΞΌs | O(log n) ~10-20ΞΌs | 2-4x faster |
| Batch Insert 1000 | ~2ms | ~15-25ms | 7-12x faster |
| Cancel Task | O(1) ~2ΞΌs | O(n) ~50-100ΞΌs | 25-50x faster |
| Postpone Task | O(1) ~4ΞΌs | O(log n) ~15-30ΞΌs | 4-7x faster |
Large Scale Testing
- β 10,000 concurrent timers
- β Creation time < 100ms
- β All timers fire correctly
Use Cases
1. Network Timeout Management
async
2. Heartbeat Detection
let timer = with_defaults;
let mut service = timer.create_service;
for client_id in client_ids
3. Cache Expiration
async
4. Game Buff System
async
// Extend buff duration
timer.postpone;
5. Retry Mechanism
async
License
This project is licensed under either of:
- MIT License (LICENSE-MIT or http://opensource.org/licenses/MIT)
- Apache License 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
Acknowledgments
The timing wheel algorithm was first proposed by George Varghese and Tony Lauck in the paper "Hashed and Hierarchical Timing Wheels" (SOSP '87).
Full Documentation: docs.rs/kestrel-timer