Skip to main content

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