Skip to main content

sz_orm_websocket/
pool.rs

1//! # 连接池管理
2//!
3//! 实现 WebSocket 连接池:限制最大连接数,LRU 淘汰最久未活跃的连接。
4//! 适用于高并发场景下的连接数控制。
5//!
6//! ## 主要类型
7//!
8//! - [`PoolConfig`] — 连接池配置
9//! - [`PooledConnection`] — 池中连接条目
10//! - [`ConnectionPool`] — LRU 连接池
11
12use std::collections::{HashMap, VecDeque};
13use std::sync::Arc;
14use tokio::sync::RwLock;
15
16/// 连接池配置
17#[derive(Debug, Clone, Copy)]
18pub struct PoolConfig {
19    /// 最大连接数
20    pub max_connections: usize,
21}
22
23impl Default for PoolConfig {
24    fn default() -> Self {
25        Self {
26            max_connections: 10_000,
27        }
28    }
29}
30
31impl PoolConfig {
32    pub fn new(max_connections: usize) -> Self {
33        Self { max_connections }
34    }
35
36    pub fn validate(&self) -> Result<(), String> {
37        if self.max_connections == 0 {
38            return Err("max_connections must be > 0".to_string());
39        }
40        Ok(())
41    }
42}
43
44/// 池中连接条目
45#[derive(Debug, Clone)]
46pub struct PooledConnection {
47    /// 连接 ID
48    pub connection_id: String,
49    /// 关联的用户 ID
50    pub user_id: Option<i64>,
51    /// 最后活跃时间戳(毫秒)
52    pub last_active_at: i64,
53    /// 创建时间戳(毫秒)
54    pub created_at: i64,
55    /// 已发送消息数
56    pub messages_sent: u64,
57    /// 已接收消息数
58    pub messages_received: u64,
59}
60
61impl PooledConnection {
62    pub fn new(connection_id: impl Into<String>, now_ms: i64) -> Self {
63        Self {
64            connection_id: connection_id.into(),
65            user_id: None,
66            last_active_at: now_ms,
67            created_at: now_ms,
68            messages_sent: 0,
69            messages_received: 0,
70        }
71    }
72
73    pub fn with_user(mut self, user_id: i64) -> Self {
74        self.user_id = Some(user_id);
75        self
76    }
77
78    /// 更新最后活跃时间
79    pub fn touch(&mut self, now_ms: i64) {
80        self.last_active_at = now_ms;
81    }
82
83    /// 记录发送消息
84    pub fn record_sent(&mut self) {
85        self.messages_sent += 1;
86    }
87
88    /// 记录接收消息
89    pub fn record_received(&mut self) {
90        self.messages_received += 1;
91    }
92
93    /// 空闲时长(毫秒)
94    pub fn idle_ms(&self, now_ms: i64) -> i64 {
95        now_ms - self.last_active_at
96    }
97
98    /// 存活时长(毫秒)
99    pub fn uptime_ms(&self, now_ms: i64) -> i64 {
100        now_ms - self.created_at
101    }
102}
103
104/// LRU 连接池
105#[derive(Debug)]
106pub struct ConnectionPool {
107    config: PoolConfig,
108    /// 连接表:connection_id -> PooledConnection
109    connections: Arc<RwLock<HashMap<String, PooledConnection>>>,
110    /// LRU 顺序队列:最近活跃的在前,最久未活跃的在后
111    lru_order: Arc<RwLock<VecDeque<String>>>,
112}
113
114/// 加入连接池的结果
115#[derive(Debug, PartialEq, Eq)]
116pub enum AdmitResult {
117    /// 连接被接纳
118    Admitted,
119    /// 连接已存在(重复添加)
120    AlreadyExists,
121    /// 池已满,淘汰了最久未活跃的连接以腾出空间
122    EvictedAndAdmitted { evicted_id: String },
123}
124
125impl ConnectionPool {
126    pub fn new(config: PoolConfig) -> Self {
127        Self {
128            config,
129            connections: Arc::new(RwLock::new(HashMap::new())),
130            lru_order: Arc::new(RwLock::new(VecDeque::new())),
131        }
132    }
133
134    /// 获取配置
135    pub fn config(&self) -> &PoolConfig {
136        &self.config
137    }
138
139    /// 尝试将连接加入池中。
140    /// - 若已存在,返回 AlreadyExists
141    /// - 若池已满,淘汰 LRU 末尾连接后接纳
142    /// - 否则接纳
143    pub async fn admit(&self, connection_id: impl Into<String>, now_ms: i64) -> AdmitResult {
144        let id = connection_id.into();
145        let mut connections = self.connections.write().await;
146        if connections.contains_key(&id) {
147            return AdmitResult::AlreadyExists;
148        }
149
150        let mut lru = self.lru_order.write().await;
151        // 池已满:淘汰 LRU 末尾
152        let evicted = if connections.len() >= self.config.max_connections {
153            // 从末尾找到一个仍存在的连接(可能已被 remove 清除)
154            let mut evicted_id = None;
155            while let Some(candidate) = lru.pop_back() {
156                if connections.contains_key(&candidate) {
157                    connections.remove(&candidate);
158                    evicted_id = Some(candidate);
159                    break;
160                }
161            }
162            evicted_id
163        } else {
164            None
165        };
166
167        // 插入新连接
168        connections.insert(id.clone(), PooledConnection::new(&id, now_ms));
169        lru.push_front(id.clone());
170
171        match evicted {
172            Some(evicted_id) => AdmitResult::EvictedAndAdmitted { evicted_id },
173            None => AdmitResult::Admitted,
174        }
175    }
176
177    /// 移除连接
178    pub async fn remove(&self, connection_id: &str) -> Option<PooledConnection> {
179        let mut connections = self.connections.write().await;
180        let removed = connections.remove(connection_id);
181        if removed.is_some() {
182            let mut lru = self.lru_order.write().await;
183            lru.retain(|id| id != connection_id);
184        }
185        removed
186    }
187
188    /// 更新连接活跃时间(移到 LRU 头部)
189    pub async fn touch(&self, connection_id: &str, now_ms: i64) -> bool {
190        let mut connections = self.connections.write().await;
191        if let Some(conn) = connections.get_mut(connection_id) {
192            conn.touch(now_ms);
193            drop(connections);
194            let mut lru = self.lru_order.write().await;
195            lru.retain(|id| id != connection_id);
196            lru.push_front(connection_id.to_string());
197            return true;
198        }
199        false
200    }
201
202    /// 记录发送消息
203    pub async fn record_sent(&self, connection_id: &str) -> bool {
204        let mut connections = self.connections.write().await;
205        if let Some(conn) = connections.get_mut(connection_id) {
206            conn.record_sent();
207            return true;
208        }
209        false
210    }
211
212    /// 记录接收消息
213    pub async fn record_received(&self, connection_id: &str) -> bool {
214        let mut connections = self.connections.write().await;
215        if let Some(conn) = connections.get_mut(connection_id) {
216            conn.record_received();
217            return true;
218        }
219        false
220    }
221
222    /// 获取连接
223    pub async fn get(&self, connection_id: &str) -> Option<PooledConnection> {
224        let connections = self.connections.read().await;
225        connections.get(connection_id).cloned()
226    }
227
228    /// 当前连接数
229    pub async fn count(&self) -> usize {
230        let connections = self.connections.read().await;
231        connections.len()
232    }
233
234    /// 是否已满
235    pub async fn is_full(&self) -> bool {
236        self.count().await >= self.config.max_connections
237    }
238
239    /// 按用户 ID 查询连接
240    pub async fn find_by_user(&self, user_id: i64) -> Vec<PooledConnection> {
241        let connections = self.connections.read().await;
242        let mut result: Vec<PooledConnection> = connections
243            .values()
244            .filter(|c| c.user_id == Some(user_id))
245            .cloned()
246            .collect();
247        result.sort_by(|a, b| a.connection_id.cmp(&b.connection_id));
248        result
249    }
250
251    /// 清理空闲超过指定时长的连接,返回被清理的数量
252    pub async fn evict_idle(&self, idle_threshold_ms: i64, now_ms: i64) -> usize {
253        let mut connections = self.connections.write().await;
254        let mut lru = self.lru_order.write().await;
255        let before = connections.len();
256        let to_remove: Vec<String> = connections
257            .iter()
258            .filter(|(_, c)| c.idle_ms(now_ms) >= idle_threshold_ms)
259            .map(|(id, _)| id.clone())
260            .collect();
261        for id in &to_remove {
262            connections.remove(id);
263        }
264        lru.retain(|id| !to_remove.contains(id));
265        before - connections.len()
266    }
267
268    /// 清空连接池
269    pub async fn clear(&self) {
270        let mut connections = self.connections.write().await;
271        let mut lru = self.lru_order.write().await;
272        connections.clear();
273        lru.clear();
274    }
275
276    /// 获取 LRU 顺序的连接 ID 列表(最近活跃在前)
277    pub async fn lru_order_list(&self) -> Vec<String> {
278        let lru = self.lru_order.read().await;
279        lru.iter().cloned().collect()
280    }
281}
282
283#[cfg(test)]
284mod tests {
285    use super::*;
286
287    #[test]
288    fn test_pool_config_default() {
289        let cfg = PoolConfig::default();
290        assert_eq!(cfg.max_connections, 10_000);
291    }
292
293    #[test]
294    fn test_pool_config_validate_ok() {
295        let cfg = PoolConfig::new(100);
296        assert!(cfg.validate().is_ok());
297        // 验证字段未被 validate 修改
298        assert_eq!(cfg.max_connections, 100, "validate 不应修改 max_connections");
299    }
300
301    #[test]
302    fn test_pool_config_validate_zero() {
303        let cfg = PoolConfig::new(0);
304        assert!(cfg.validate().is_err());
305    }
306
307    #[test]
308    fn test_pooled_connection_new() {
309        let conn = PooledConnection::new("c1", 1000);
310        assert_eq!(conn.connection_id, "c1");
311        assert!(conn.user_id.is_none());
312        assert_eq!(conn.last_active_at, 1000);
313        assert_eq!(conn.created_at, 1000);
314        assert_eq!(conn.messages_sent, 0);
315        assert_eq!(conn.messages_received, 0);
316    }
317
318    #[test]
319    fn test_pooled_connection_with_user() {
320        let conn = PooledConnection::new("c1", 1000).with_user(42);
321        assert_eq!(conn.user_id, Some(42));
322    }
323
324    #[test]
325    fn test_pooled_connection_touch_updates_last_active() {
326        let mut conn = PooledConnection::new("c1", 1000);
327        conn.touch(2000);
328        assert_eq!(conn.last_active_at, 2000);
329    }
330
331    #[test]
332    fn test_pooled_connection_record_sent_and_received() {
333        let mut conn = PooledConnection::new("c1", 1000);
334        conn.record_sent();
335        conn.record_sent();
336        conn.record_received();
337        assert_eq!(conn.messages_sent, 2);
338        assert_eq!(conn.messages_received, 1);
339    }
340
341    #[test]
342    fn test_pooled_connection_idle_ms() {
343        let conn = PooledConnection::new("c1", 1000);
344        assert_eq!(conn.idle_ms(1500), 500);
345    }
346
347    #[test]
348    fn test_pooled_connection_uptime_ms() {
349        let conn = PooledConnection::new("c1", 1000);
350        assert_eq!(conn.uptime_ms(3000), 2000);
351    }
352
353    #[tokio::test]
354    async fn test_pool_admit_new_connection() {
355        let pool = ConnectionPool::new(PoolConfig::new(10));
356        let result = pool.admit("c1", 1000).await;
357        assert_eq!(result, AdmitResult::Admitted);
358        assert_eq!(pool.count().await, 1);
359    }
360
361    #[tokio::test]
362    async fn test_pool_admit_duplicate_returns_already_exists() {
363        let pool = ConnectionPool::new(PoolConfig::new(10));
364        pool.admit("c1", 1000).await;
365        let result = pool.admit("c1", 2000).await;
366        assert_eq!(result, AdmitResult::AlreadyExists);
367        assert_eq!(pool.count().await, 1);
368    }
369
370    #[tokio::test]
371    async fn test_pool_admit_evicts_lru_when_full() {
372        let pool = ConnectionPool::new(PoolConfig::new(2));
373        pool.admit("c1", 1000).await;
374        pool.admit("c2", 2000).await;
375        // c1 更早活跃,应被淘汰
376        let result = pool.admit("c3", 3000).await;
377        match result {
378            AdmitResult::EvictedAndAdmitted { evicted_id } => {
379                assert_eq!(evicted_id, "c1");
380            }
381            _ => panic!("expected EvictedAndAdmitted, got {:?}", result),
382        }
383        assert_eq!(pool.count().await, 2);
384        assert!(pool.get("c1").await.is_none());
385        assert!(pool.get("c2").await.is_some());
386        assert!(pool.get("c3").await.is_some());
387    }
388
389    #[tokio::test]
390    async fn test_pool_admit_touch_updates_lru_order() {
391        let pool = ConnectionPool::new(PoolConfig::new(2));
392        pool.admit("c1", 1000).await;
393        pool.admit("c2", 2000).await;
394        // touch c1 使其成为最近活跃
395        pool.touch("c1", 5000).await;
396        // 现在 c2 是最久未活跃的
397        let result = pool.admit("c3", 6000).await;
398        match result {
399            AdmitResult::EvictedAndAdmitted { evicted_id } => {
400                assert_eq!(evicted_id, "c2");
401            }
402            _ => panic!("expected c2 to be evicted"),
403        }
404    }
405
406    #[tokio::test]
407    async fn test_pool_remove() {
408        let pool = ConnectionPool::new(PoolConfig::new(10));
409        pool.admit("c1", 1000).await;
410        let removed = pool.remove("c1").await;
411        assert!(removed.is_some());
412        assert_eq!(pool.count().await, 0);
413    }
414
415    #[tokio::test]
416    async fn test_pool_remove_missing_returns_none() {
417        let pool = ConnectionPool::new(PoolConfig::new(10));
418        assert!(pool.remove("ghost").await.is_none());
419    }
420
421    #[tokio::test]
422    async fn test_pool_touch_updates_last_active() {
423        let pool = ConnectionPool::new(PoolConfig::new(10));
424        pool.admit("c1", 1000).await;
425        pool.touch("c1", 5000).await;
426        let conn = pool.get("c1").await.unwrap();
427        assert_eq!(conn.last_active_at, 5000);
428    }
429
430    #[tokio::test]
431    async fn test_pool_touch_unknown_returns_false() {
432        let pool = ConnectionPool::new(PoolConfig::new(10));
433        assert!(!pool.touch("ghost", 1000).await);
434    }
435
436    #[tokio::test]
437    async fn test_pool_record_sent_and_received() {
438        let pool = ConnectionPool::new(PoolConfig::new(10));
439        pool.admit("c1", 1000).await;
440        assert!(pool.record_sent("c1").await);
441        assert!(pool.record_received("c1").await);
442        let conn = pool.get("c1").await.unwrap();
443        assert_eq!(conn.messages_sent, 1);
444        assert_eq!(conn.messages_received, 1);
445    }
446
447    #[tokio::test]
448    async fn test_pool_record_sent_unknown_returns_false() {
449        let pool = ConnectionPool::new(PoolConfig::new(10));
450        assert!(!pool.record_sent("ghost").await);
451    }
452
453    #[tokio::test]
454    async fn test_pool_find_by_user() {
455        let pool = ConnectionPool::new(PoolConfig::new(10));
456        pool.admit("c1", 1000).await;
457        pool.admit("c2", 1000).await;
458        // 设置用户 ID
459        {
460            let mut conns = pool.connections.write().await;
461            conns.get_mut("c1").unwrap().user_id = Some(100);
462            conns.get_mut("c2").unwrap().user_id = Some(200);
463        }
464        let found = pool.find_by_user(100).await;
465        assert_eq!(found.len(), 1);
466        assert_eq!(found[0].connection_id, "c1");
467    }
468
469    #[tokio::test]
470    async fn test_pool_find_by_user_none() {
471        let pool = ConnectionPool::new(PoolConfig::new(10));
472        pool.admit("c1", 1000).await;
473        let found = pool.find_by_user(999).await;
474        assert!(found.is_empty());
475    }
476
477    #[tokio::test]
478    async fn test_pool_evict_idle() {
479        let pool = ConnectionPool::new(PoolConfig::new(10));
480        pool.admit("c1", 1000).await;
481        pool.admit("c2", 2000).await;
482        pool.admit("c3", 5000).await;
483        // 清理空闲超过 3000ms 的连接(now=6000)
484        // c1 idle=5000, c2 idle=4000, c3 idle=1000
485        let evicted = pool.evict_idle(3000, 6000).await;
486        assert_eq!(evicted, 2); // c1 和 c2
487        assert_eq!(pool.count().await, 1);
488        assert!(pool.get("c3").await.is_some());
489    }
490
491    #[tokio::test]
492    async fn test_pool_evict_idle_none() {
493        let pool = ConnectionPool::new(PoolConfig::new(10));
494        pool.admit("c1", 1000).await;
495        // 空闲阈值很大,不应清理
496        let evicted = pool.evict_idle(100_000, 2000).await;
497        assert_eq!(evicted, 0);
498    }
499
500    #[tokio::test]
501    async fn test_pool_clear() {
502        let pool = ConnectionPool::new(PoolConfig::new(10));
503        pool.admit("c1", 1000).await;
504        pool.admit("c2", 2000).await;
505        pool.clear().await;
506        assert_eq!(pool.count().await, 0);
507    }
508
509    #[tokio::test]
510    async fn test_pool_is_full() {
511        let pool = ConnectionPool::new(PoolConfig::new(2));
512        assert!(!pool.is_full().await);
513        pool.admit("c1", 1000).await;
514        assert!(!pool.is_full().await);
515        pool.admit("c2", 2000).await;
516        assert!(pool.is_full().await);
517    }
518
519    #[tokio::test]
520    async fn test_pool_lru_order_list() {
521        let pool = ConnectionPool::new(PoolConfig::new(10));
522        pool.admit("c1", 1000).await;
523        pool.admit("c2", 2000).await;
524        pool.admit("c3", 3000).await;
525        // 初始顺序:c3, c2, c1(最近在前)
526        let order = pool.lru_order_list().await;
527        assert_eq!(order, vec!["c3", "c2", "c1"]);
528        // touch c1
529        pool.touch("c1", 4000).await;
530        let order2 = pool.lru_order_list().await;
531        assert_eq!(order2, vec!["c1", "c3", "c2"]);
532    }
533
534    #[tokio::test]
535    async fn test_pool_remove_updates_lru_order() {
536        let pool = ConnectionPool::new(PoolConfig::new(10));
537        pool.admit("c1", 1000).await;
538        pool.admit("c2", 2000).await;
539        pool.admit("c3", 3000).await;
540        pool.remove("c2").await;
541        let order = pool.lru_order_list().await;
542        assert_eq!(order, vec!["c3", "c1"]);
543    }
544
545    #[tokio::test]
546    async fn test_pool_admit_after_evict_maintains_count() {
547        let pool = ConnectionPool::new(PoolConfig::new(1));
548        pool.admit("c1", 1000).await;
549        pool.admit("c2", 2000).await; // 淘汰 c1
550        pool.admit("c3", 3000).await; // 淘汰 c2
551        assert_eq!(pool.count().await, 1);
552        assert!(pool.get("c3").await.is_some());
553    }
554}