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!(
299            cfg.max_connections, 100,
300            "validate 不应修改 max_connections"
301        );
302    }
303
304    #[test]
305    fn test_pool_config_validate_zero() {
306        let cfg = PoolConfig::new(0);
307        assert!(cfg.validate().is_err());
308    }
309
310    #[test]
311    fn test_pooled_connection_new() {
312        let conn = PooledConnection::new("c1", 1000);
313        assert_eq!(conn.connection_id, "c1");
314        assert!(conn.user_id.is_none());
315        assert_eq!(conn.last_active_at, 1000);
316        assert_eq!(conn.created_at, 1000);
317        assert_eq!(conn.messages_sent, 0);
318        assert_eq!(conn.messages_received, 0);
319    }
320
321    #[test]
322    fn test_pooled_connection_with_user() {
323        let conn = PooledConnection::new("c1", 1000).with_user(42);
324        assert_eq!(conn.user_id, Some(42));
325    }
326
327    #[test]
328    fn test_pooled_connection_touch_updates_last_active() {
329        let mut conn = PooledConnection::new("c1", 1000);
330        conn.touch(2000);
331        assert_eq!(conn.last_active_at, 2000);
332    }
333
334    #[test]
335    fn test_pooled_connection_record_sent_and_received() {
336        let mut conn = PooledConnection::new("c1", 1000);
337        conn.record_sent();
338        conn.record_sent();
339        conn.record_received();
340        assert_eq!(conn.messages_sent, 2);
341        assert_eq!(conn.messages_received, 1);
342    }
343
344    #[test]
345    fn test_pooled_connection_idle_ms() {
346        let conn = PooledConnection::new("c1", 1000);
347        assert_eq!(conn.idle_ms(1500), 500);
348    }
349
350    #[test]
351    fn test_pooled_connection_uptime_ms() {
352        let conn = PooledConnection::new("c1", 1000);
353        assert_eq!(conn.uptime_ms(3000), 2000);
354    }
355
356    #[tokio::test]
357    async fn test_pool_admit_new_connection() {
358        let pool = ConnectionPool::new(PoolConfig::new(10));
359        let result = pool.admit("c1", 1000).await;
360        assert_eq!(result, AdmitResult::Admitted);
361        assert_eq!(pool.count().await, 1);
362    }
363
364    #[tokio::test]
365    async fn test_pool_admit_duplicate_returns_already_exists() {
366        let pool = ConnectionPool::new(PoolConfig::new(10));
367        pool.admit("c1", 1000).await;
368        let result = pool.admit("c1", 2000).await;
369        assert_eq!(result, AdmitResult::AlreadyExists);
370        assert_eq!(pool.count().await, 1);
371    }
372
373    #[tokio::test]
374    async fn test_pool_admit_evicts_lru_when_full() {
375        let pool = ConnectionPool::new(PoolConfig::new(2));
376        pool.admit("c1", 1000).await;
377        pool.admit("c2", 2000).await;
378        // c1 更早活跃,应被淘汰
379        let result = pool.admit("c3", 3000).await;
380        match result {
381            AdmitResult::EvictedAndAdmitted { evicted_id } => {
382                assert_eq!(evicted_id, "c1");
383            }
384            _ => panic!("expected EvictedAndAdmitted, got {:?}", result),
385        }
386        assert_eq!(pool.count().await, 2);
387        assert!(pool.get("c1").await.is_none());
388        assert!(pool.get("c2").await.is_some());
389        assert!(pool.get("c3").await.is_some());
390    }
391
392    #[tokio::test]
393    async fn test_pool_admit_touch_updates_lru_order() {
394        let pool = ConnectionPool::new(PoolConfig::new(2));
395        pool.admit("c1", 1000).await;
396        pool.admit("c2", 2000).await;
397        // touch c1 使其成为最近活跃
398        pool.touch("c1", 5000).await;
399        // 现在 c2 是最久未活跃的
400        let result = pool.admit("c3", 6000).await;
401        match result {
402            AdmitResult::EvictedAndAdmitted { evicted_id } => {
403                assert_eq!(evicted_id, "c2");
404            }
405            _ => panic!("expected c2 to be evicted"),
406        }
407    }
408
409    #[tokio::test]
410    async fn test_pool_remove() {
411        let pool = ConnectionPool::new(PoolConfig::new(10));
412        pool.admit("c1", 1000).await;
413        let removed = pool.remove("c1").await;
414        assert!(removed.is_some());
415        assert_eq!(pool.count().await, 0);
416    }
417
418    #[tokio::test]
419    async fn test_pool_remove_missing_returns_none() {
420        let pool = ConnectionPool::new(PoolConfig::new(10));
421        assert!(pool.remove("ghost").await.is_none());
422    }
423
424    #[tokio::test]
425    async fn test_pool_touch_updates_last_active() {
426        let pool = ConnectionPool::new(PoolConfig::new(10));
427        pool.admit("c1", 1000).await;
428        pool.touch("c1", 5000).await;
429        let conn = pool.get("c1").await.unwrap();
430        assert_eq!(conn.last_active_at, 5000);
431    }
432
433    #[tokio::test]
434    async fn test_pool_touch_unknown_returns_false() {
435        let pool = ConnectionPool::new(PoolConfig::new(10));
436        assert!(!pool.touch("ghost", 1000).await);
437    }
438
439    #[tokio::test]
440    async fn test_pool_record_sent_and_received() {
441        let pool = ConnectionPool::new(PoolConfig::new(10));
442        pool.admit("c1", 1000).await;
443        assert!(pool.record_sent("c1").await);
444        assert!(pool.record_received("c1").await);
445        let conn = pool.get("c1").await.unwrap();
446        assert_eq!(conn.messages_sent, 1);
447        assert_eq!(conn.messages_received, 1);
448    }
449
450    #[tokio::test]
451    async fn test_pool_record_sent_unknown_returns_false() {
452        let pool = ConnectionPool::new(PoolConfig::new(10));
453        assert!(!pool.record_sent("ghost").await);
454    }
455
456    #[tokio::test]
457    async fn test_pool_find_by_user() {
458        let pool = ConnectionPool::new(PoolConfig::new(10));
459        pool.admit("c1", 1000).await;
460        pool.admit("c2", 1000).await;
461        // 设置用户 ID
462        {
463            let mut conns = pool.connections.write().await;
464            conns.get_mut("c1").unwrap().user_id = Some(100);
465            conns.get_mut("c2").unwrap().user_id = Some(200);
466        }
467        let found = pool.find_by_user(100).await;
468        assert_eq!(found.len(), 1);
469        assert_eq!(found[0].connection_id, "c1");
470    }
471
472    #[tokio::test]
473    async fn test_pool_find_by_user_none() {
474        let pool = ConnectionPool::new(PoolConfig::new(10));
475        pool.admit("c1", 1000).await;
476        let found = pool.find_by_user(999).await;
477        assert!(found.is_empty());
478    }
479
480    #[tokio::test]
481    async fn test_pool_evict_idle() {
482        let pool = ConnectionPool::new(PoolConfig::new(10));
483        pool.admit("c1", 1000).await;
484        pool.admit("c2", 2000).await;
485        pool.admit("c3", 5000).await;
486        // 清理空闲超过 3000ms 的连接(now=6000)
487        // c1 idle=5000, c2 idle=4000, c3 idle=1000
488        let evicted = pool.evict_idle(3000, 6000).await;
489        assert_eq!(evicted, 2); // c1 和 c2
490        assert_eq!(pool.count().await, 1);
491        assert!(pool.get("c3").await.is_some());
492    }
493
494    #[tokio::test]
495    async fn test_pool_evict_idle_none() {
496        let pool = ConnectionPool::new(PoolConfig::new(10));
497        pool.admit("c1", 1000).await;
498        // 空闲阈值很大,不应清理
499        let evicted = pool.evict_idle(100_000, 2000).await;
500        assert_eq!(evicted, 0);
501    }
502
503    #[tokio::test]
504    async fn test_pool_clear() {
505        let pool = ConnectionPool::new(PoolConfig::new(10));
506        pool.admit("c1", 1000).await;
507        pool.admit("c2", 2000).await;
508        pool.clear().await;
509        assert_eq!(pool.count().await, 0);
510    }
511
512    #[tokio::test]
513    async fn test_pool_is_full() {
514        let pool = ConnectionPool::new(PoolConfig::new(2));
515        assert!(!pool.is_full().await);
516        pool.admit("c1", 1000).await;
517        assert!(!pool.is_full().await);
518        pool.admit("c2", 2000).await;
519        assert!(pool.is_full().await);
520    }
521
522    #[tokio::test]
523    async fn test_pool_lru_order_list() {
524        let pool = ConnectionPool::new(PoolConfig::new(10));
525        pool.admit("c1", 1000).await;
526        pool.admit("c2", 2000).await;
527        pool.admit("c3", 3000).await;
528        // 初始顺序:c3, c2, c1(最近在前)
529        let order = pool.lru_order_list().await;
530        assert_eq!(order, vec!["c3", "c2", "c1"]);
531        // touch c1
532        pool.touch("c1", 4000).await;
533        let order2 = pool.lru_order_list().await;
534        assert_eq!(order2, vec!["c1", "c3", "c2"]);
535    }
536
537    #[tokio::test]
538    async fn test_pool_remove_updates_lru_order() {
539        let pool = ConnectionPool::new(PoolConfig::new(10));
540        pool.admit("c1", 1000).await;
541        pool.admit("c2", 2000).await;
542        pool.admit("c3", 3000).await;
543        pool.remove("c2").await;
544        let order = pool.lru_order_list().await;
545        assert_eq!(order, vec!["c3", "c1"]);
546    }
547
548    #[tokio::test]
549    async fn test_pool_admit_after_evict_maintains_count() {
550        let pool = ConnectionPool::new(PoolConfig::new(1));
551        pool.admit("c1", 1000).await;
552        pool.admit("c2", 2000).await; // 淘汰 c1
553        pool.admit("c3", 3000).await; // 淘汰 c2
554        assert_eq!(pool.count().await, 1);
555        assert!(pool.get("c3").await.is_some());
556    }
557}