use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum LifecycleEvent {
ChannelPollStarted,
ChannelPollSucceeded,
ChannelPollFailed,
ChannelBackoffArmed,
ChannelBackoffReset,
ChannelHeartbeatPersistFailed,
ConfigLocked,
CheckpointOutcomeRecorded,
PhaseStarted,
PhaseCompleted,
PhaseCancelled,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ChannelPollStartedPayload {
pub channel_kind: String,
pub channel_slug: String,
pub since_rfc3339: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ChannelPollSucceededPayload {
pub channel_kind: String,
pub channel_slug: String,
pub envelope_count: usize,
pub previous_backoff_attempt: u32,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ChannelPollFailedPayload {
pub channel_kind: String,
pub channel_slug: String,
pub error_class: String,
pub error_message: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ChannelBackoffArmedPayload {
pub channel_kind: String,
pub channel_slug: String,
pub attempt: u32,
pub step_ms: u64,
pub delay_ms: u64,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ChannelBackoffResetPayload {
pub channel_kind: String,
pub channel_slug: String,
pub previous_backoff_attempt: u32,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ChannelHeartbeatPersistFailedPayload {
pub channel_kind: String,
pub channel_slug: String,
pub error: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ConfigLockedPayload {
pub key: String,
pub value: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CheckpointOutcomeRecordedPayload {
pub wal_pages: u64,
pub warn_pages: u64,
pub high_water_pages: u64,
pub truncate_high_water_pages: u64,
pub above_warn: bool,
pub above_high_water: bool,
pub above_truncate_high_water: bool,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PhaseStartedPayload {
pub work_class: String,
pub phase: String,
pub corpus_size: Option<u64>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PhaseCompletedPayload {
pub work_class: String,
pub phase: String,
pub wall_us: i64,
pub cpu_us: Option<i64>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PhaseCancelledPayload {
pub work_class: String,
pub phase: String,
pub wall_us: i64,
pub cpu_us: Option<i64>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn lifecycle_event_roundtrips_through_json() {
for kind in [
LifecycleEvent::ChannelPollStarted,
LifecycleEvent::ChannelPollSucceeded,
LifecycleEvent::ChannelPollFailed,
LifecycleEvent::ChannelBackoffArmed,
LifecycleEvent::ChannelBackoffReset,
LifecycleEvent::ChannelHeartbeatPersistFailed,
LifecycleEvent::ConfigLocked,
LifecycleEvent::CheckpointOutcomeRecorded,
LifecycleEvent::PhaseStarted,
LifecycleEvent::PhaseCompleted,
LifecycleEvent::PhaseCancelled,
] {
let json = serde_json::to_string(&kind).expect("serialize");
let parsed: LifecycleEvent = serde_json::from_str(&json).expect("deserialize");
assert_eq!(parsed, kind);
}
}
#[test]
fn checkpoint_outcome_recorded_payload_roundtrips() {
let payload = CheckpointOutcomeRecordedPayload {
wal_pages: 2500,
warn_pages: 2000,
high_water_pages: 6000,
truncate_high_water_pages: 20_000,
above_warn: true,
above_high_water: false,
above_truncate_high_water: false,
};
let json = serde_json::to_value(&payload).expect("serialize");
let parsed: CheckpointOutcomeRecordedPayload =
serde_json::from_value(json).expect("deserialize");
assert_eq!(parsed, payload);
}
#[test]
fn phase_started_payload_roundtrips() {
let payload = PhaseStartedPayload {
work_class: "warm".into(),
phase: "ann_warm".into(),
corpus_size: Some(553_000),
};
let json = serde_json::to_value(&payload).expect("serialize");
let parsed: PhaseStartedPayload = serde_json::from_value(json).expect("deserialize");
assert_eq!(parsed, payload);
}
#[test]
fn phase_completed_payload_roundtrips_with_absent_cpu_us() {
let payload = PhaseCompletedPayload {
work_class: "warm".into(),
phase: "ann_warm".into(),
wall_us: 41_000_000,
cpu_us: None,
};
let json = serde_json::to_value(&payload).expect("serialize");
let parsed: PhaseCompletedPayload = serde_json::from_value(json).expect("deserialize");
assert_eq!(parsed, payload);
}
#[test]
fn phase_cancelled_payload_roundtrips() {
let payload = PhaseCancelledPayload {
work_class: "warm".into(),
phase: "ann_warm".into(),
wall_us: 12_000,
cpu_us: Some(9_500),
};
let json = serde_json::to_value(&payload).expect("serialize");
let parsed: PhaseCancelledPayload = serde_json::from_value(json).expect("deserialize");
assert_eq!(parsed, payload);
}
}