tdm_server_rust/cache/rss_file_lock.rs
1//! RSS 文件 per-file 互斥锁
2//!
3//! 防止多个定时任务同时写入同一 RSS XML 文件导致内容交错。
4//! 基于 `DashMap` 实现懒创建的文件级互斥锁。
5//!
6//! ## 设计
7//!
8//! 每次 RSS 写入前,通过文件名获取对应的 `tokio::sync::Mutex`。
9//! 若该文件尚无锁,自动创建并插入 `DashMap`。
10//! 锁的生命周期为整个写入过程(读数据库 + 格式化 + 写文件)。
11
12use dashmap::DashMap;
13use std::sync::Arc;
14use tokio::sync::{Mutex, OwnedMutexGuard};
15
16/// RSS 文件级互斥锁管理器
17///
18/// 内部使用 `DashMap<String, Arc<Mutex<()>>>` 维护文件名到互斥锁的映射。
19/// 锁在首次访问时懒创建,进程生命周期内持续存在。
20pub struct RssFileLock {
21 /// 文件名 → 互斥锁的并发哈希表
22 locks: DashMap<String, Arc<Mutex<()>>>,
23}
24
25impl RssFileLock {
26 /// 创建空的锁表
27 pub fn new() -> Self {
28 Self {
29 locks: DashMap::new(),
30 }
31 }
32
33 /// 获取指定文件的独占写锁
34 ///
35 /// 若该文件尚无锁,自动插入新的 `Mutex`。
36 /// 返回 `OwnedMutexGuard`,锁在 guard drop 时释放。
37 ///
38 /// # 参数
39 ///
40 /// - `file_name`: RSS XML 文件名(如 `"rss.xml"`)
41 ///
42 /// # 返回值
43 ///
44 /// 返回持有锁的 `OwnedMutexGuard`,离开作用域时自动释放。
45 pub async fn lock(&self, file_name: &str) -> OwnedMutexGuard<()> {
46 let mutex = self
47 .locks
48 .entry(file_name.to_string())
49 .or_insert_with(|| Arc::new(Mutex::new(())))
50 .clone();
51 mutex.lock_owned().await
52 }
53}
54
55impl Default for RssFileLock {
56 fn default() -> Self {
57 Self::new()
58 }
59}