1use std::collections::{HashMap, VecDeque};
13use std::sync::Arc;
14use tokio::sync::RwLock;
15
16#[derive(Debug, Clone, Copy)]
18pub struct PoolConfig {
19 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#[derive(Debug, Clone)]
46pub struct PooledConnection {
47 pub connection_id: String,
49 pub user_id: Option<i64>,
51 pub last_active_at: i64,
53 pub created_at: i64,
55 pub messages_sent: u64,
57 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 pub fn touch(&mut self, now_ms: i64) {
80 self.last_active_at = now_ms;
81 }
82
83 pub fn record_sent(&mut self) {
85 self.messages_sent += 1;
86 }
87
88 pub fn record_received(&mut self) {
90 self.messages_received += 1;
91 }
92
93 pub fn idle_ms(&self, now_ms: i64) -> i64 {
95 now_ms - self.last_active_at
96 }
97
98 pub fn uptime_ms(&self, now_ms: i64) -> i64 {
100 now_ms - self.created_at
101 }
102}
103
104#[derive(Debug)]
106pub struct ConnectionPool {
107 config: PoolConfig,
108 connections: Arc<RwLock<HashMap<String, PooledConnection>>>,
110 lru_order: Arc<RwLock<VecDeque<String>>>,
112}
113
114#[derive(Debug, PartialEq, Eq)]
116pub enum AdmitResult {
117 Admitted,
119 AlreadyExists,
121 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 pub fn config(&self) -> &PoolConfig {
136 &self.config
137 }
138
139 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 let evicted = if connections.len() >= self.config.max_connections {
153 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 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 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 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 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 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 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 pub async fn count(&self) -> usize {
230 let connections = self.connections.read().await;
231 connections.len()
232 }
233
234 pub async fn is_full(&self) -> bool {
236 self.count().await >= self.config.max_connections
237 }
238
239 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 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 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 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 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 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 pool.touch("c1", 5000).await;
399 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 {
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 let evicted = pool.evict_idle(3000, 6000).await;
489 assert_eq!(evicted, 2); 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 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 let order = pool.lru_order_list().await;
530 assert_eq!(order, vec!["c3", "c2", "c1"]);
531 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; pool.admit("c3", 3000).await; assert_eq!(pool.count().await, 1);
555 assert!(pool.get("c3").await.is_some());
556 }
557}