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!(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 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 pool.touch("c1", 5000).await;
396 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 {
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 let evicted = pool.evict_idle(3000, 6000).await;
486 assert_eq!(evicted, 2); 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 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 let order = pool.lru_order_list().await;
527 assert_eq!(order, vec!["c3", "c2", "c1"]);
528 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; pool.admit("c3", 3000).await; assert_eq!(pool.count().await, 1);
552 assert!(pool.get("c3").await.is_some());
553 }
554}