Skip to main content

wowlab_sentinel/cron/
integrity.rs

1use async_trait::async_trait;
2
3use super::CronJob;
4use crate::state::ServerState;
5
6const RANKING_STALE_THRESHOLD_HOURS: f64 = 24.0;
7
8#[derive(Debug)]
9pub(crate) struct IntegrityCheckJob {
10    schedule: String,
11}
12
13impl IntegrityCheckJob {
14    pub(crate) fn new(schedule: &str) -> Self {
15        Self {
16            schedule: schedule.to_owned(),
17        }
18    }
19}
20
21#[async_trait]
22impl CronJob for IntegrityCheckJob {
23    fn name(&self) -> &'static str {
24        "integrity_check"
25    }
26
27    fn schedule(&self) -> &str {
28        &self.schedule
29    }
30
31    async fn run(&self, state: &ServerState) {
32        if let Err(e) = run_integrity_check(state).await {
33            tracing::error!(error = %e, "Integrity check failed");
34        }
35    }
36}
37
38async fn run_integrity_check(
39    state: &ServerState,
40) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
41    let active_count: i64 = sqlx::query_file_scalar!("queries/integrity_active_seasons.sql")
42        .fetch_one(state.dbs.get::<crate::cron::CronDb>())
43        .await?;
44
45    match active_count {
46        1 => tracing::debug!("Integrity: active season count OK"),
47        0 => tracing::warn!("Integrity: no active season found"),
48        n => tracing::error!(count = n, "Integrity: multiple active seasons"),
49    }
50
51    let orphaned: i64 = sqlx::query_file_scalar!("queries/integrity_orphaned_jobs.sql")
52        .fetch_one(state.dbs.get::<crate::cron::CronDb>())
53        .await?;
54
55    if orphaned > 0 {
56        tracing::warn!(
57            count = orphaned,
58            "Integrity: completed jobs without season_id"
59        );
60    }
61
62    let stale_hours: Option<f64> =
63        sqlx::query_file_scalar!("queries/integrity_ranking_freshness.sql")
64            .fetch_one(state.dbs.get::<crate::cron::CronDb>())
65            .await?;
66
67    if let Some(hours) = stale_hours {
68        if hours > RANKING_STALE_THRESHOLD_HOURS {
69            tracing::warn!(hours_stale = hours, "Integrity: rankings are stale");
70        }
71    }
72
73    tracing::info!(
74        active_seasons = active_count,
75        orphaned_jobs = orphaned,
76        "Integrity check completed"
77    );
78
79    Ok(())
80}