Skip to main content

sa_token_core/cleanup/
background.rs

1// Author: 金书记 | Author: Jin Shuji
2//! Optional interval cleanup. Disabled by default — never auto-spawn from Manager::new.
3//! 可选定时清理。默认关闭 —— 禁止在 Manager::new 里自动 spawn。
4
5use std::sync::Arc;
6use std::time::Duration;
7
8use tokio::sync::watch;
9use tokio::task::JoinHandle;
10use tokio::time;
11
12use crate::nonce::NonceManager;
13use crate::online::OnlineManager;
14
15/// Cleanup switches | 清理开关
16#[derive(Debug, Clone)]
17pub struct CleanupConfig {
18    /// Master switch; false means spawn() returns a no-op handle.
19    /// 总开关;false 时 spawn 返回空操作句柄。
20    pub enabled: bool,
21    /// `interval` | `interval`
22    pub interval: Duration,
23    /// `cleanup_nonce` | `cleanup_nonce`
24    pub cleanup_nonce: bool,
25    /// Best-effort prune of online indexes (list members whose record expired).
26    /// 尽力修剪在线索引(记录已过期的列表成员)。
27    pub cleanup_online: bool,
28}
29
30impl Default for CleanupConfig {
31    fn default() -> Self {
32        Self {
33            enabled: false,
34            interval: Duration::from_secs(300),
35            cleanup_nonce: true,
36            cleanup_online: true,
37        }
38    }
39}
40
41/// Handle that can be asked to stop without aborting in-flight IO blindly.
42/// 可协作停止的句柄,避免直接 abort 中断进行中的 IO。
43pub struct BackgroundCleanupTask {
44    stop: watch::Sender<bool>,
45    /// Kept so the task is not detached without a way to observe join later.
46    /// 保留句柄,便于后续观察 join;协作停止靠 `stop` 信号。
47    #[allow(dead_code)]
48    handle: Option<JoinHandle<()>>,
49}
50
51impl std::fmt::Debug for BackgroundCleanupTask {
52    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53        f.write_str("BackgroundCleanupTask { .. }")
54    }
55}
56
57impl BackgroundCleanupTask {
58    /// `spawn` — spawn | `spawn`
59    pub fn spawn(
60        config: CleanupConfig,
61        nonce: Option<Arc<NonceManager>>,
62        online: Option<Arc<OnlineManager>>,
63    ) -> Self {
64        let (stop, rx) = watch::channel(false);
65        if !config.enabled {
66            return Self { stop, handle: None };
67        }
68
69        let handle = tokio::spawn(async move {
70            let mut ticker = time::interval(config.interval);
71            ticker.set_missed_tick_behavior(time::MissedTickBehavior::Delay);
72            let mut rx = rx;
73            loop {
74                tokio::select! {
75                    _ = ticker.tick() => {
76                        if config.cleanup_nonce {
77                            if let Some(n) = &nonce {
78                                if let Err(e) = n.cleanup_expired().await {
79                                    tracing::warn!(error = %e, "nonce cleanup failed");
80                                }
81                            }
82                        }
83                        if config.cleanup_online {
84                            if let Some(o) = &online {
85                                match o.get_online_users().await {
86                                    Ok(users) => {
87                                        for uid in users {
88                                            if let Err(e) = o.get_user_sessions(&uid).await {
89                                                tracing::warn!(error = %e, login_id = %uid, "online prune failed");
90                                            }
91                                        }
92                                    }
93                                    Err(e) => tracing::warn!(error = %e, "online list failed during cleanup"),
94                                }
95                            }
96                        }
97                    }
98                    _ = rx.changed() => {
99                        if *rx.borrow() {
100                            break;
101                        }
102                    }
103                }
104            }
105        });
106
107        Self {
108            stop,
109            handle: Some(handle),
110        }
111    }
112
113    /// Cooperative stop | 协作停止
114    pub fn shutdown(&self) {
115        let _ = self.stop.send(true);
116    }
117}
118
119impl Drop for BackgroundCleanupTask {
120    fn drop(&mut self) {
121        let _ = self.stop.send(true);
122    }
123}