Skip to main content

max / makenotwork

3.6 KB · 95 lines History Blame Raw
1 //! Background backup freshness check task.
2
3 use tokio::task::JoinHandle;
4 use tracing::info;
5
6 use pom::alerts::Alerter;
7 use pom::checks::backup;
8 use pom::config::Config;
9 use pom::db;
10
11 use super::{CheckInterval, configured_targets};
12
13 pub(crate) fn spawn_backup_tasks(
14 config: &Config,
15 pool: &sqlx::SqlitePool,
16 cancel: &tokio_util::sync::CancellationToken,
17 alerter: Option<&Alerter>,
18 ) -> Vec<JoinHandle<()>> {
19 let mut handles = Vec::new();
20
21 for (name, target_config) in configured_targets(config) {
22 if let Some(backup_config) = target_config.backups {
23 let pool = pool.clone();
24 let name = name.clone();
25 let label = target_config.label.clone();
26 let alerter = alerter.cloned();
27 let cancel = cancel.clone();
28 let interval_secs = backup_config.interval_secs;
29
30 info!(
31 "{name}: backup check every {interval_secs}s (dir={}, databases={:?})",
32 backup_config.directory, backup_config.databases
33 );
34
35 handles.push(tokio::spawn(async move {
36 let mut ticks = CheckInterval::new(interval_secs, cancel);
37 while ticks.next().await {
38 for database in &backup_config.databases {
39 let result = backup::check_backup(
40 &name,
41 &backup_config.directory,
42 database,
43 backup_config.max_age_hours,
44 );
45 info!(
46 "{}: backup {}, {} (age: {}h)",
47 name,
48 database,
49 result.status,
50 result.age_hours.unwrap_or(-1),
51 );
52
53 // Check for status transitions before storing
54 let previous = db::get_latest_backup_check(&pool, &name, database)
55 .await
56 .ok()
57 .flatten();
58
59 if let Err(e) = db::insert_backup_check(&pool, &result).await {
60 tracing::error!(
61 "{name}: failed to store backup check for {database}: {e}"
62 );
63 }
64
65 // Fire alerts on status transitions
66 if let Some(ref alerter) = alerter {
67 let prev_status = previous.as_ref().map(|p| p.status.as_str());
68 let was_ok = prev_status.is_none_or(|s| s == "ok");
69 let now_ok = result.status == "ok";
70
71 if was_ok && !now_ok {
72 // Transitioned to a bad state
73 alerter
74 .send_backup_stale_alert(
75 &name,
76 &label,
77 database,
78 &result.status,
79 result.age_hours,
80 )
81 .await;
82 } else if !was_ok && now_ok {
83 // Recovered
84 alerter.send_backup_recovery(&name, &label, database).await;
85 }
86 }
87 }
88 }
89 }));
90 }
91 }
92
93 handles
94 }
95