rustio_admin/background.rs
1//! Background tasks. A tiny runner that spawns one tokio task per
2//! recurring job. Kept simple on purpose — we don't ship a cron DSL,
3//! just "run this every N seconds".
4
5use std::time::Duration;
6
7use tokio::time::{interval, MissedTickBehavior};
8
9use crate::auth::purge_expired_sessions;
10use crate::auth::recovery::purge_expired_reset_tokens;
11use crate::orm::Db;
12
13// public:
14/// Spin up the standard housekeeping tasks. Call this once during
15/// app startup. Returns immediately — the tasks run forever.
16pub fn spawn_housekeeping(db: Db) {
17 spawn_session_sweeper(db.clone());
18}
19
20// public:
21/// Periodic maintenance loop — every 10 minutes, sweep:
22///
23/// 1. Expired sessions (`auth::purge_expired_sessions`) — removes
24/// rows from `rustio_sessions` where `expires_at < NOW()`.
25/// 2. Reset-token rows past their forensic-retention window
26/// (`auth::recovery::purge_expired_reset_tokens` — R1 commit
27/// #12) — removes rows from `rustio_password_reset_tokens`
28/// where `expires_at < NOW() - INTERVAL '7 days'`.
29///
30/// **Failure isolation:** the two sweeps run sequentially within
31/// each tick but each handles its own `Result` — a failure in one
32/// does NOT prevent the other from running, and neither prevents
33/// the next tick. The next tick happens unconditionally on the
34/// timer.
35///
36/// The 10-minute interval is kept from R0; reset-token sweeping
37/// piggy-backs on the existing tick rather than spawning a second
38/// timer loop.
39///
40/// (The function name is preserved for backwards compatibility —
41/// downstream projects already call `spawn_session_sweeper`
42/// directly. The expanded scope is documented above.)
43pub fn spawn_session_sweeper(db: Db) {
44 log::info!("background housekeeping sweeper spawned (10 min interval)");
45 tokio::spawn(async move {
46 let mut tick = interval(Duration::from_secs(600));
47 tick.set_missed_tick_behavior(MissedTickBehavior::Delay);
48 // Skip the first tick (it fires immediately) — no need to
49 // sweep the second the server boots.
50 tick.tick().await;
51 loop {
52 tick.tick().await;
53
54 // Session sweep — independent of the recovery-token
55 // sweep below. A failure here does NOT prevent the
56 // recovery sweep from running on this same tick.
57 match purge_expired_sessions(&db).await {
58 Ok(0) => {}
59 Ok(n) => log::info!("swept {n} expired sessions"),
60 Err(e) => log::warn!("session sweep failed: {e}"),
61 }
62
63 // Recovery-token sweep — independent of the session
64 // sweep above. A failure here does NOT prevent the
65 // session sweep from running, AND does not prevent
66 // the next tick. The subsystem-tagged target lets
67 // operators filter by stream.
68 match purge_expired_reset_tokens(&db).await {
69 Ok(0) => {}
70 Ok(n) => log::info!(
71 target: "rustio_admin::recovery_sweeper",
72 "swept {n} expired reset tokens"
73 ),
74 Err(e) => log::warn!(
75 target: "rustio_admin::recovery_sweeper",
76 "recovery-token sweep failed: {e}"
77 ),
78 }
79 }
80 });
81}