Skip to main content

aion_store/
timer.rs

1//! `TimerEntry` and timer-facing types.
2
3use aion_core::{TimerId, WorkflowId};
4use chrono::{DateTime, Utc};
5use serde::{Deserialize, Serialize};
6
7/// Durable timer record returned by [`crate::ReadableEventStore::expired_timers`].
8#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)]
9pub struct TimerEntry {
10    /// Workflow that owns the timer.
11    pub workflow_id: WorkflowId,
12    /// Timer identifier within the owning workflow.
13    pub timer_id: TimerId,
14    /// Instant at which the timer is due to fire.
15    pub fire_at: DateTime<Utc>,
16    /// The workflow-history sequence of the `TimerStarted` event that armed
17    /// this row, or `0` for an arming that records no `TimerStarted` (the
18    /// schedule coordinator's trigger timers — sequence numbers start at 1,
19    /// so `0` can never collide with a recorded arming).
20    ///
21    /// This is the row-identity component that makes value-conditional
22    /// retirement discriminate a RE-ARM TO THE IDENTICAL INSTANT: without it,
23    /// a named timer re-armed to the same `fire_at` encodes byte-identically
24    /// to the consumed arming, so a stale retirement deletes the live
25    /// replacement's row — and a past-due replacement is never re-armed by
26    /// the boot sweep (which only restores future armings), a permanently
27    /// lost wake. Every re-arm records a new `TimerStarted` with a strictly
28    /// higher sequence, so `(fire_at, armed_seq)` is unique per arming. The
29    /// store treats the value as an opaque identity component: it never
30    /// orders or interprets it.
31    pub armed_seq: u64,
32}
33
34/// The outcome of a value-conditional
35/// [`retire_timer`](crate::ReadableEventStore::retire_timer) call.
36///
37/// Both variants are success — the distinction exists so callers (the boot
38/// sweep's counters in particular) can report what actually happened to the
39/// row instead of counting attempts as retirements.
40#[derive(Clone, Copy, Debug, PartialEq, Eq)]
41pub enum TimerRetirement {
42    /// The key no longer holds the retired arming's row: it was deleted by
43    /// this call, or was already gone (never scheduled, or retired earlier —
44    /// the idempotent shape). A distributed backend reports an absent key as
45    /// `Retired` too: its tombstone lands unconditionally on an empty key.
46    Retired,
47    /// A DIFFERENT arming now owns the key (same timer name, new
48    /// `(fire_at, armed_seq)` identity — possibly the SAME instant re-armed
49    /// under a newer `TimerStarted`). The replacement's row was left
50    /// untouched — it is the re-armed timer's only durable claim to a
51    /// recovery fire.
52    Superseded,
53}