1use std::collections::VecDeque;
2use std::future::Future;
3use std::sync::Arc;
4use std::time::{Duration, Instant};
5use tokio::sync::{Mutex, Semaphore};
6use tokio::task::JoinHandle;
7use tokio::time::{interval, timeout};
8
9pub const DEFAULT_MAX_SIZE: usize = 10;
10pub const DEFAULT_IDLE_TIMEOUT: Duration = Duration::from_secs(5 * 60); pub const DEFAULT_CONNECTION_TIMEOUT: Duration = Duration::from_secs(10); pub const DEFAULT_CLEANUP_INTERVAL: Duration = Duration::from_secs(30); pub const MAX_IDLE_KEEP: usize = 8;
18
19#[derive(Clone)]
21pub struct CleanupConfig {
22 pub interval: Duration,
23 pub enabled: bool,
24}
25
26impl Default for CleanupConfig {
27 fn default() -> Self {
28 Self {
29 interval: DEFAULT_CLEANUP_INTERVAL,
30 enabled: true,
31 }
32 }
33}
34
35struct CleanupTaskController {
37 handle: Option<JoinHandle<()>>,
38 shutdown_tx: Option<tokio::sync::oneshot::Sender<()>>,
39}
40
41impl CleanupTaskController {
42 fn new() -> Self {
43 Self {
44 handle: None,
45 shutdown_tx: None,
46 }
47 }
48
49 fn start<T, M>(
50 &mut self,
51 connections: Arc<Mutex<VecDeque<InnerConnection<T>>>>,
52 max_idle_time: Duration,
53 cleanup_interval: Duration,
54 manager: Arc<M>,
55 max_size: usize,
56 ) where
57 T: Send + 'static,
58 M: ConnectionManager<Connection = T> + Send + Sync + 'static,
59 {
60 if self.handle.is_some() {
61 log::warn!("Cleanup task is already running");
62 return;
63 }
64 let (shutdown_tx, mut shutdown_rx) = tokio::sync::oneshot::channel();
65 self.shutdown_tx = Some(shutdown_tx);
66 let handle = tokio::spawn(async move {
67 let mut interval_timer = interval(cleanup_interval);
68 log::info!("Background cleanup task started with interval: {cleanup_interval:?}");
69
70 loop {
71 tokio::select! {
72 _ = interval_timer.tick() => {}
73 _ = &mut shutdown_rx => {
74 log::info!("Received shutdown signal, exiting cleanup loop");
75 break;
76 }
77 };
78
79 let candidates: VecDeque<_> = {
84 let mut connections = connections.lock().await;
85 connections.drain(..).collect()
86 };
87 let initial_count = candidates.len();
88 let now = Instant::now();
89
90 let mut valid_connections = VecDeque::new();
91 let mut candidates = candidates;
92 let mut shutdown_requested = false;
93 while let Some(mut conn) = candidates.pop_front() {
94 let not_expired = now.duration_since(conn.created_at) < max_idle_time;
95 let is_valid = if not_expired {
96 let validation = tokio::select! {
97 _ = &mut shutdown_rx => None,
98 is_valid = manager.is_valid(&mut conn.connection) => Some(is_valid),
99 };
100 let Some(is_valid) = validation else {
101 valid_connections.append(&mut candidates);
103 shutdown_requested = true;
104 break;
105 };
106 is_valid
107 } else {
108 false
109 };
110 if not_expired && is_valid {
111 valid_connections.push_back(conn);
112 }
113 }
114 let removed_count = initial_count - valid_connections.len();
115
116 {
119 let idle_cap = max_size.min(MAX_IDLE_KEEP);
120 let mut connections = connections.lock().await;
121 for conn in valid_connections {
122 if connections.len() < idle_cap {
123 connections.push_back(conn);
124 }
125 }
126 log::debug!("Current pool (remaining {}/{idle_cap}) after cleanup", connections.len());
127 }
128
129 if removed_count > 0 {
130 log::debug!("Background cleanup removed {removed_count} expired/invalid connections");
131 }
132
133 if shutdown_requested {
134 break;
135 }
136 }
137 });
138
139 self.handle = Some(handle);
140 }
141
142 async fn stop(&mut self) {
143 if let Some(shutdown_tx) = self.shutdown_tx.take() {
144 let _ = shutdown_tx.send(());
145 }
146 if let Some(handle) = self.handle.take() {
147 if let Err(e) = handle.await {
148 log::error!("Error while stopping background cleanup task: {e}");
149 }
150 log::info!("Background cleanup task stopped in async stop");
151 }
152 }
153
154 fn stop_sync(&mut self) {
155 if let Some(shutdown_tx) = self.shutdown_tx.take() {
156 let _ = shutdown_tx.send(());
157 }
158 if let Some(handle) = self.handle.take() {
159 std::thread::sleep(std::time::Duration::from_millis(100));
160 handle.abort();
161 log::info!("Background cleanup task stopped in stop_sync");
162 }
163 }
164}
165
166impl Drop for CleanupTaskController {
167 fn drop(&mut self) {
168 self.stop_sync();
169 }
170}
171
172pub trait ConnectionManager: Sync + Send + Clone {
174 type Connection: Send;
176
177 type Error: std::error::Error + Send + Sync + 'static;
179
180 type CreateFut: Future<Output = Result<Self::Connection, Self::Error>> + Send;
182
183 type ValidFut<'a>: Future<Output = bool> + Send
185 where
186 Self: 'a;
187
188 fn create_connection(&self) -> Self::CreateFut;
190
191 fn is_valid<'a>(&'a self, connection: &'a mut Self::Connection) -> Self::ValidFut<'a>;
193}
194
195pub struct ConnectionPool<M: ConnectionManager> {
197 connections: Arc<Mutex<VecDeque<InnerConnection<M::Connection>>>>,
198 semaphore: Arc<Semaphore>,
199 max_size: usize,
200 manager: M,
201 max_idle_time: Duration,
202 connection_timeout: Duration,
203 cleanup_controller: Arc<Mutex<CleanupTaskController>>,
204 outstanding_count: Arc<std::sync::atomic::AtomicUsize>,
205}
206
207struct InnerConnection<T> {
209 pub connection: T,
210 pub created_at: Instant,
211}
212
213pub struct ManagedConnection<M>
215where
216 M: ConnectionManager + Send + Sync + 'static,
217{
218 connection: Option<M::Connection>,
219 pool: Arc<ConnectionPool<M>>,
220 _permit: tokio::sync::OwnedSemaphorePermit,
221}
222
223impl<M: ConnectionManager> ManagedConnection<M> {
224 pub fn into_inner(mut self) -> M::Connection {
226 self.connection.take().unwrap()
227 }
228
229 fn new(connection: M::Connection, pool: Arc<ConnectionPool<M>>, permit: tokio::sync::OwnedSemaphorePermit) -> Self {
230 pool.outstanding_count.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
231 ManagedConnection {
232 connection: Some(connection),
233 pool,
234 _permit: permit,
235 }
236 }
237}
238
239impl<M> ConnectionPool<M>
240where
241 M: ConnectionManager + Send + Sync + Clone + 'static,
242{
243 pub fn new(
245 max_size: Option<usize>,
246 max_idle_time: Option<Duration>,
247 connection_timeout: Option<Duration>,
248 cleanup_config: Option<CleanupConfig>,
249 manager: M,
250 ) -> Arc<Self> {
251 let max_size = max_size.unwrap_or(DEFAULT_MAX_SIZE);
252 let max_idle_time = max_idle_time.unwrap_or(DEFAULT_IDLE_TIMEOUT);
253 let connection_timeout = connection_timeout.unwrap_or(DEFAULT_CONNECTION_TIMEOUT);
254 let cleanup_config = cleanup_config.unwrap_or_default();
255
256 log::info!(
257 "Creating connection pool with max_size: {max_size}, idle_timeout: {max_idle_time:?}, connection_timeout: {connection_timeout:?}, cleanup_enabled: {}",
258 cleanup_config.enabled
259 );
260
261 let connections = Arc::new(Mutex::new(VecDeque::new()));
262 let cleanup_controller = Arc::new(Mutex::new(CleanupTaskController::new()));
263
264 let pool = Arc::new(ConnectionPool {
265 connections: connections.clone(),
266 semaphore: Arc::new(Semaphore::new(max_size)),
267 max_size,
268 manager,
269 max_idle_time,
270 connection_timeout,
271 cleanup_controller: cleanup_controller.clone(),
272 outstanding_count: Arc::new(std::sync::atomic::AtomicUsize::new(0)),
273 });
274
275 if cleanup_config.enabled {
277 let manager = Arc::new(pool.manager.clone());
278 tokio::spawn(async move {
279 let mut controller = cleanup_controller.lock().await;
280 controller.start(connections, max_idle_time, cleanup_config.interval, manager, max_size);
281 });
282 }
283
284 pool
285 }
286
287 pub async fn get_connection(self: Arc<Self>) -> Result<ManagedConnection<M>, PoolError<M::Error>> {
289 log::debug!("Attempting to get connection from pool");
290
291 let permit = self.semaphore.clone().acquire_owned().await.map_err(|_| PoolError::PoolClosed)?;
293
294 loop {
299 let candidate = {
300 let mut connections = self.connections.lock().await;
301 connections.pop_front()
302 };
303 let Some(mut pooled_conn) = candidate else {
304 break;
306 };
307 log::trace!("Found existing connection in pool, validating...");
308 let age = Instant::now().duration_since(pooled_conn.created_at);
309 let is_valid = if age < self.max_idle_time {
310 let r = self.manager.is_valid(&mut pooled_conn.connection).await;
311 if !r {
312 log::warn!("Connection validation failed, discarding invalid connection");
313 }
314 r
315 } else {
316 log::debug!("Connection expired (age: {age:?}), discarding");
317 false
318 };
319 if is_valid {
320 let size = self.connections.lock().await.len();
321 log::debug!("Reusing existing connection from pool (remaining: {size}/{})", self.max_size);
322 return Ok(ManagedConnection::new(pooled_conn.connection, self.clone(), permit));
323 }
324 }
325
326 log::trace!("No valid connection available, creating new connection...");
327 match timeout(self.connection_timeout, self.manager.create_connection()).await {
329 Ok(Ok(connection)) => {
330 log::info!("Successfully created new connection");
331 Ok(ManagedConnection::new(connection, self.clone(), permit))
332 }
333 Ok(Err(e)) => {
334 log::error!("Failed to create new connection");
335 Err(PoolError::Creation(e))
336 }
337 Err(_) => {
338 log::warn!("Connection creation timed out after {:?}", self.connection_timeout);
339 Err(PoolError::Timeout)
340 }
341 }
342 }
343
344 pub fn outstanding_count(&self) -> usize {
345 self.outstanding_count.load(std::sync::atomic::Ordering::SeqCst)
346 }
347
348 pub async fn pool_size(&self) -> usize {
349 self.connections.lock().await.len()
350 }
351
352 pub fn max_size(&self) -> usize {
353 self.max_size
354 }
355
356 pub async fn stop_cleanup_task(&self) {
358 let mut controller = self.cleanup_controller.lock().await;
359 controller.stop().await;
360 }
361
362 pub async fn restart_cleanup_task(&self, cleanup_config: CleanupConfig) {
364 self.stop_cleanup_task().await;
365
366 if cleanup_config.enabled {
367 let manager = Arc::new(self.manager.clone());
368 let mut controller = self.cleanup_controller.lock().await;
369 let m = self.max_size;
370 controller.start(self.connections.clone(), self.max_idle_time, cleanup_config.interval, manager, m);
371 }
372 }
373}
374
375impl<M: ConnectionManager> ConnectionPool<M> {
376 async fn recycle(&self, mut connection: M::Connection) {
377 let idle_cap = self.max_size.min(MAX_IDLE_KEEP);
382 {
383 let connections = self.connections.lock().await;
384 if connections.len() >= idle_cap {
385 log::debug!("Idle pool full ({idle_cap}), dropping connection");
386 return;
387 }
388 }
389 if !self.manager.is_valid(&mut connection).await {
390 log::debug!("Invalid connection, dropping");
391 return;
392 }
393 let mut connections = self.connections.lock().await;
394 if connections.len() < idle_cap {
395 connections.push_back(InnerConnection {
396 connection,
397 created_at: Instant::now(),
398 });
399 log::debug!("Connection recycled to pool (pool size: {}/{})", connections.len(), idle_cap);
400 } else {
401 log::debug!("Idle pool full ({idle_cap}), dropping connection");
402 }
403 }
404}
405
406impl<M: ConnectionManager> Drop for ManagedConnection<M> {
407 fn drop(&mut self) {
408 self.pool.outstanding_count.fetch_sub(1, std::sync::atomic::Ordering::SeqCst);
409 if let Some(connection) = self.connection.take() {
410 let pool = self.pool.clone();
411 _ = tokio::spawn(async move {
412 log::trace!("Recycling connection to pool on drop");
413 pool.recycle(connection).await;
414 });
415 }
416 }
417}
418
419impl<M: ConnectionManager> AsRef<M::Connection> for ManagedConnection<M> {
421 fn as_ref(&self) -> &M::Connection {
422 self.connection.as_ref().unwrap()
423 }
424}
425
426impl<M: ConnectionManager> AsMut<M::Connection> for ManagedConnection<M> {
427 fn as_mut(&mut self) -> &mut M::Connection {
428 self.connection.as_mut().unwrap()
429 }
430}
431
432impl<M: ConnectionManager> std::ops::Deref for ManagedConnection<M> {
434 type Target = M::Connection;
435
436 fn deref(&self) -> &Self::Target {
437 self.connection.as_ref().unwrap()
438 }
439}
440
441impl<M: ConnectionManager> std::ops::DerefMut for ManagedConnection<M> {
442 fn deref_mut(&mut self) -> &mut Self::Target {
443 self.connection.as_mut().unwrap()
444 }
445}
446
447#[derive(Debug)]
449pub enum PoolError<E> {
450 PoolClosed,
451 Timeout,
452 Creation(E),
453}
454
455impl<E: std::fmt::Display> std::fmt::Display for PoolError<E> {
456 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
457 match self {
458 PoolError::PoolClosed => write!(f, "Connection pool is closed"),
459 PoolError::Timeout => write!(f, "Connection creation timeout"),
460 PoolError::Creation(e) => write!(f, "Connection creation failed: {e}"),
461 }
462 }
463}
464
465impl<E: std::error::Error + 'static> std::error::Error for PoolError<E> {
466 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
467 match self {
468 PoolError::Creation(e) => Some(e),
469 _ => None,
470 }
471 }
472}