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