timer
timer
¶
Timer wheel — all timing in the baton flows through here.
The timer wheel is a priority queue of future events. A background drain
task sleeps until the next timer fires, puts the event into the baton's
inbox, and repeats. Components never call asyncio.sleep() for scheduling
purposes — they schedule a timer instead.
Eight timing responsibilities converge here: - Retry backoff delays - Rate limit recovery waits - Circuit breaker recovery timers - Stale detection idle timeouts - Inter-sheet pacing delays - Concert cooldown between chained jobs - Job wall-clock timeouts - Cron ticks
Design:
- Priority queue via heapq — O(log n) schedule/fire.
- Cancelled timers are lazily skipped on fire (tombstone pattern),
not eagerly removed — O(1) cancel.
- The drain task is woken by asyncio.Event when timers change,
so adding an earlier timer doesn't wait for the previous sleep.
- All public methods are synchronous (no await needed to schedule).
Only run() and shutdown() are async.
See: docs/plans/2026-03-26-baton-design.md — Timer Wheel section.
Classes¶
TimerHandle
dataclass
¶
Opaque handle returned by schedule(). Used for cancellation.
Attributes:
| Name | Type | Description |
|---|---|---|
fire_at |
float
|
Monotonic time when the event should fire. |
event |
BatonEvent
|
The BatonEvent to deliver to the inbox. |
TimerWheel
¶
Priority queue of future events with a background drain task.
Usage::
inbox = asyncio.Queue()
wheel = TimerWheel(inbox)
# Schedule a retry in 30 seconds
handle = wheel.schedule(30.0, RetryDue(job_id="j1", sheet_num=5))
# Cancel if no longer needed
wheel.cancel(handle)
# Run the drain task (usually as asyncio.create_task(wheel.run()))
await wheel.run() # blocks forever, fires events into inbox
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
inbox
|
Queue[Any]
|
The asyncio.Queue that receives fired events. This is the baton's event inbox — the same queue that receives musician results, external commands, etc. |
required |
Source code in src/marianne/daemon/baton/timer.py
Attributes¶
Functions¶
schedule
¶
Schedule an event to fire after delay_seconds.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
delay_seconds
|
float
|
Seconds from now. Clamped to >= 0. |
required |
event
|
BatonEvent
|
The BatonEvent to deliver when the timer fires. |
required |
Returns:
| Type | Description |
|---|---|
TimerHandle
|
A TimerHandle that can be passed to |
Source code in src/marianne/daemon/baton/timer.py
cancel
¶
Cancel a scheduled timer.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
handle
|
TimerHandle
|
The handle returned by |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if the timer was pending and is now cancelled. |
bool
|
False if it was already cancelled or already fired. |
Source code in src/marianne/daemon/baton/timer.py
snapshot
¶
Export pending (non-cancelled) timers for persistence.
Returns a list of (fire_at, event) tuples — the data needed to reconstruct the timer wheel after a restart.
Source code in src/marianne/daemon/baton/timer.py
run
async
¶
Background drain task — fire timers into the inbox.
This coroutine runs indefinitely. Cancel the task to stop it. It sleeps until the next timer is due (or a wake signal arrives), fires all due timers, and repeats.
Source code in src/marianne/daemon/baton/timer.py
shutdown
async
¶
Fire all pending timers immediately and stop.
Used during graceful shutdown — ensures no events are lost. Events are placed into the inbox in fire_at order.