1use std::collections::HashMap;
19use std::error::Error;
20use std::hash::Hash;
21use std::sync::{Arc, Mutex, OnceLock, Weak};
22use std::time::Duration;
23
24use tokio::sync::Notify;
25use tokio::task::JoinHandle;
26use tokio::time::timeout as tokio_timeout;
27
28use crate::core::clock::{Clock, SystemClock};
29use crate::core::connection::{CloseReason, PooledConnection};
30use crate::core::errors::{FactoryError, PoolError};
31use crate::core::timeouts::{Timeout, remaining};
32use crate::factories::async_factory::AsyncConnectionFactory;
33use crate::observability::events::{Listeners, PoolListener};
34use crate::observability::stats::{PoolStats, StatsCollector, StatsGauges};
35use crate::policy::config::PoolConfig;
36use crate::policy::lifecycle::{
37 borrow_reason, needs_validation, new_connection, pop_idle, rotation_reason, sweep,
38};
39use crate::pools::sync_pool::PoolOptions;
40use crate::resilience::breaker::{BreakerState, CircuitBreaker};
41use crate::rng::{Rng, SystemRng};
42
43struct State<T> {
44 idle: Vec<PooledConnection<T>>,
45 checked_out: HashMap<T, PooledConnection<T>>,
46 size: usize,
47 waiters: usize,
48 closed: bool,
49 opened: bool,
50}
51
52struct Inner<T, F> {
53 name: String,
54 factory: F,
55 cfg: PoolConfig,
56 clock: Arc<dyn Clock>,
57 rng: Mutex<Box<dyn Rng>>,
58
59 state: Mutex<State<T>>,
60 notify: Notify,
61
62 stats: Arc<StatsCollector<T>>,
63 events: Listeners<T>,
64 breaker: OnceLock<CircuitBreaker>,
65
66 stop: Notify,
67 handle: Mutex<Option<JoinHandle<()>>>,
68}
69
70pub struct AsyncConnectionPool<T, F>(Arc<Inner<T, F>>);
72
73impl<T, F> Clone for AsyncConnectionPool<T, F> {
74 fn clone(&self) -> Self {
75 AsyncConnectionPool(self.0.clone())
76 }
77}
78
79struct SlotGuard<T, F> {
85 inner: Arc<Inner<T, F>>,
86 armed: bool,
87}
88
89impl<T, F> SlotGuard<T, F>
90where
91 T: Clone + Eq + Hash + Send + Sync + 'static,
92 F: AsyncConnectionFactory<T> + 'static,
93{
94 fn new(inner: Arc<Inner<T, F>>) -> Self {
95 Self { inner, armed: true }
96 }
97 fn commit(mut self) {
98 self.armed = false;
99 }
100}
101
102impl<T, F> Drop for SlotGuard<T, F> {
103 fn drop(&mut self) {
104 if self.armed {
105 let mut g = self.inner.state.lock().unwrap();
106 g.size -= 1;
107 self.inner.notify.notify_waiters();
108 }
109 }
110}
111
112struct BorrowGuard<T, F>
117where
118 T: Clone + Eq + Hash + Send + Sync + 'static,
119 F: AsyncConnectionFactory<T> + 'static,
120{
121 inner: Arc<Inner<T, F>>,
122 key: T,
123 armed: bool,
124}
125
126impl<T, F> BorrowGuard<T, F>
127where
128 T: Clone + Eq + Hash + Send + Sync + 'static,
129 F: AsyncConnectionFactory<T> + 'static,
130{
131 fn new(inner: Arc<Inner<T, F>>, key: T) -> Self {
132 Self {
133 inner,
134 key,
135 armed: true,
136 }
137 }
138 fn commit(mut self) {
139 self.armed = false;
140 }
141}
142
143impl<T, F> Drop for BorrowGuard<T, F>
144where
145 T: Clone + Eq + Hash + Send + Sync + 'static,
146 F: AsyncConnectionFactory<T> + 'static,
147{
148 fn drop(&mut self) {
149 if !self.armed {
150 return;
151 }
152 let mut g = self.inner.state.lock().unwrap();
153 let mut p = g
156 .checked_out
157 .remove(&self.key)
158 .expect("borrowed connection present until guard commits");
159 if g.closed {
160 g.size -= 1;
163 } else {
164 let now = self.inner.clock.monotonic();
165 p.mark_returned(now);
166 g.idle.push(p);
167 }
168 self.inner.notify.notify_waiters();
169 }
170}
171
172impl<T, F> AsyncConnectionPool<T, F>
173where
174 T: Clone + Eq + Hash + Send + Sync + 'static,
175 F: AsyncConnectionFactory<T> + 'static,
176{
177 pub fn new(
179 factory: F,
180 config: PoolConfig,
181 options: Option<PoolOptions<T>>,
182 ) -> Result<Self, PoolError> {
183 config.validate()?;
184 let options = options.unwrap_or_default();
185 let clock: Arc<dyn Clock> = options.clock.unwrap_or_else(|| Arc::new(SystemClock::new()));
186 let rng: Box<dyn Rng> = options.rng.unwrap_or_else(|| Box::new(SystemRng::new()));
187 let name = options.name.unwrap_or_else(|| "pool".to_string());
188
189 let stats = Arc::new(StatsCollector::new());
190 let mut listeners: Vec<Arc<dyn PoolListener<T>>> = vec![stats.clone()];
191 listeners.extend(options.listeners);
192 let events = Listeners::new(listeners);
193
194 let inner = Arc::new(Inner {
195 name,
196 factory,
197 cfg: config,
198 clock,
199 rng: Mutex::new(rng),
200 state: Mutex::new(State {
201 idle: Vec::new(),
202 checked_out: HashMap::new(),
203 size: 0,
204 waiters: 0,
205 closed: false,
206 opened: false,
207 }),
208 notify: Notify::new(),
209 stats,
210 events,
211 breaker: OnceLock::new(),
212 stop: Notify::new(),
213 handle: Mutex::new(None),
214 });
215
216 if let Some(bc) = inner.cfg.breaker {
217 let weak = Arc::downgrade(&inner);
218 let cb = Box::new(move |old: BreakerState, new: BreakerState| {
219 if let Some(inner) = weak.upgrade() {
220 inner.events.breaker_change(old, new);
221 }
222 });
223 let breaker = CircuitBreaker::new(bc, Some(inner.clock.clone()), Some(cb));
224 let _ = inner.breaker.set(breaker);
225 }
226
227 Ok(AsyncConnectionPool(inner))
228 }
229
230 pub fn name(&self) -> &str {
232 &self.0.name
233 }
234
235 pub async fn open(&self) -> Result<(), PoolError> {
237 let want_thread = {
238 let mut g = self.0.state.lock().unwrap();
239 if g.closed {
240 return Err(PoolError::Closed(format!("pool {:?} is closed", self.0.name)));
241 }
242 if g.opened {
243 return Ok(());
244 }
245 g.opened = true;
246 self.0.cfg.maintenance_interval
247 };
248 self.0.fill_to_min().await;
249 if let Some(interval) = want_thread {
250 if !self.0.state.lock().unwrap().closed {
251 self.start_maintenance(interval);
252 }
253 }
254 Ok(())
255 }
256
257 fn start_maintenance(&self, interval: f64) {
258 let weak: Weak<Inner<T, F>> = Arc::downgrade(&self.0);
259 let handle = tokio::spawn(async move {
260 let mut ticker = tokio::time::interval(Duration::from_secs_f64(interval));
261 ticker.tick().await; loop {
263 let inner = match weak.upgrade() {
264 Some(i) => i,
265 None => return,
266 };
267 let stop = inner.stop.notified();
268 tokio::pin!(stop);
269 tokio::select! {
270 _ = ticker.tick() => {}
271 _ = &mut stop => return,
272 }
273 inner.run_maintenance_safe().await;
274 }
275 });
276 *self.0.handle.lock().unwrap() = Some(handle);
277 }
278
279 pub async fn close(&self, timeout: Option<f64>) {
282 self.0.close(timeout).await;
283 }
284
285 pub fn is_closed(&self) -> bool {
287 self.0.state.lock().unwrap().closed
288 }
289
290 pub async fn acquire(&self, timeout: Timeout) -> Result<T, PoolError> {
292 self.0.acquire(timeout).await
293 }
294
295 pub async fn release(&self, conn: T) -> Result<(), PoolError> {
297 self.0.release(conn, false, None).await
298 }
299
300 pub async fn discard(&self, conn: T) -> Result<(), PoolError> {
302 self.0.release(conn, true, None).await
303 }
304
305 pub async fn release_with_error(
307 &self,
308 conn: T,
309 error: &(dyn Error + 'static),
310 ) -> Result<(), PoolError> {
311 self.0.release(conn, false, Some(error)).await
312 }
313
314 pub fn stats(&self) -> PoolStats {
316 self.0.stats_snapshot()
317 }
318
319 pub async fn run_maintenance(&self) {
321 self.0.run_maintenance().await;
322 }
323}
324
325enum Taken<T> {
326 Reused(T),
327 Reserved,
328}
329
330impl<T, F> Inner<T, F>
331where
332 T: Clone + Eq + Hash + Send + Sync + 'static,
333 F: AsyncConnectionFactory<T> + 'static,
334{
335 fn is_closed(&self) -> bool {
336 self.state.lock().unwrap().closed
337 }
338
339 fn new_conn(&self, raw: T, now: f64) -> PooledConnection<T> {
340 let mut rng = self.rng.lock().unwrap();
341 new_connection(raw, &self.cfg, now, &mut **rng)
342 }
343
344 fn retry_delay(&self, attempt: u32) -> f64 {
345 let mut rng = self.rng.lock().unwrap();
346 self.cfg.retry.delay_for(attempt, &mut **rng)
347 }
348
349 fn free_slot(&self) {
350 let mut g = self.state.lock().unwrap();
351 g.size -= 1;
352 self.notify.notify_waiters();
353 }
354
355 async fn close(&self, timeout: Option<f64>) {
356 let idle = {
357 let mut g = self.state.lock().unwrap();
358 if g.closed {
359 return;
360 }
361 g.closed = true;
362 let idle = std::mem::take(&mut g.idle);
363 self.notify.notify_waiters();
364 idle
365 };
366 if let Some(t) = timeout {
367 let deadline = self.clock.monotonic() + t;
368 loop {
369 if self.state.lock().unwrap().checked_out.is_empty() {
370 break;
371 }
372 let left = deadline - self.clock.monotonic();
373 if left <= 0.0 {
374 break;
375 }
376 let n = self.notify.notified();
377 tokio::pin!(n);
378 n.as_mut().enable();
379 let _ = tokio_timeout(Duration::from_secs_f64(left), n).await;
380 }
381 }
382 self.stop.notify_one();
385 let handle = self.handle.lock().unwrap().take();
386 if let Some(h) = handle {
387 let _ = h.await;
388 }
389 for pooled in idle {
390 self.destroy_detached(pooled, CloseReason::PoolClosed).await;
391 }
392 }
393
394 async fn acquire(self: &Arc<Self>, timeout: Timeout) -> Result<T, PoolError> {
395 let started = self.clock.monotonic();
396 let deadline = timeout.compute_deadline(self.cfg.acquire_timeout, started);
397 let key = match self.acquire_loop(deadline).await {
398 Ok(k) => k,
399 Err(e) => {
400 if matches!(e, PoolError::Timeout(_)) {
401 let waited = self.clock.monotonic() - started;
402 self.events.safe(|l| l.on_acquire_timeout(waited));
403 }
404 return Err(e);
405 }
406 };
407 let now = self.clock.monotonic();
408 let (raw, ev, wait) = {
409 let mut g = self.state.lock().unwrap();
410 let pooled = g.checked_out.get_mut(&key).expect("acquired conn present");
411 pooled.mark_checked_out(now);
412 (pooled.raw.clone(), pooled.clone(), now - started)
413 };
414 self.events.safe(|l| l.on_acquired(&ev, wait));
415 Ok(raw)
416 }
417
418 async fn acquire_loop(self: &Arc<Self>, deadline: Option<f64>) -> Result<T, PoolError> {
419 loop {
420 match self.take_idle_or_reserve(deadline).await? {
421 Taken::Reserved => {
422 let slot = SlotGuard::new(self.clone());
423 let (pooled, created_in) = self.create(deadline).await?;
424 slot.commit();
425 return self.register_new(pooled, created_in).await;
426 }
427 Taken::Reused(key) => {
428 let guard = BorrowGuard::new(self.clone(), key.clone());
429 match self.check_borrow(&key).await {
430 None => {
431 guard.commit();
432 return Ok(key);
433 }
434 Some(reason) => {
435 guard.commit();
436 self.destroy_by_key(&key, reason).await;
437 }
438 }
439 }
440 }
441 }
442 }
443
444 async fn take_idle_or_reserve(&self, deadline: Option<f64>) -> Result<Taken<T>, PoolError> {
445 loop {
446 let notified = {
447 let mut g = self.state.lock().unwrap();
448 if g.closed {
449 return Err(PoolError::Closed(format!("pool {:?} is closed", self.name)));
450 }
451 if let Some(conn) = pop_idle(&mut g.idle, self.cfg.idle_strategy) {
452 let key = conn.raw.clone();
453 g.checked_out.insert(key.clone(), conn);
454 return Ok(Taken::Reused(key));
455 }
456 if g.size < self.cfg.max_size {
457 g.size += 1;
458 return Ok(Taken::Reserved);
459 }
460 let left = remaining(deadline, self.clock.monotonic());
461 if let Some(l) = left {
462 if l <= 0.0 {
463 return Err(PoolError::Timeout(format!(
464 "pool {:?}: no connection available (max_size={})",
465 self.name, self.cfg.max_size
466 )));
467 }
468 }
469 if let Some(max) = self.cfg.max_waiters {
470 if g.waiters >= max {
471 return Err(PoolError::Exhausted(format!(
472 "pool {:?}: waiter queue full (max_waiters={})",
473 self.name, max
474 )));
475 }
476 }
477 g.waiters += 1;
478 let n = self.notify.notified();
479 (n, left)
480 };
481 let (n, left) = notified;
482 tokio::pin!(n);
483 n.as_mut().enable();
484 let _guard = WaiterGuard { inner: self };
485 match left {
486 None => n.await,
487 Some(l) => {
488 let _ = tokio_timeout(Duration::from_secs_f64(l.max(0.0)), n).await;
489 }
490 }
491 }
492 }
493
494 async fn check_borrow(&self, key: &T) -> Option<CloseReason> {
495 let now = self.clock.monotonic();
496 let (reason, need_validate, raw) = {
497 let g = self.state.lock().unwrap();
498 let pooled = g.checked_out.get(key).expect("conn present");
499 (
500 borrow_reason(pooled, &self.cfg, now),
501 needs_validation(pooled, &self.cfg, now),
502 pooled.raw.clone(),
503 )
504 };
505 if let Some(r) = reason {
506 return Some(r);
507 }
508 if need_validate && !self.is_healthy(&raw).await {
509 return Some(CloseReason::Unhealthy);
510 }
511 None
512 }
513
514 async fn release(
515 &self,
516 conn: T,
517 discard: bool,
518 error: Option<&(dyn Error + 'static)>,
519 ) -> Result<(), PoolError> {
520 let pooled = {
521 let mut g = self.state.lock().unwrap();
522 g.checked_out.remove(&conn)
523 };
524 let pooled = match pooled {
525 Some(p) => p,
526 None => {
527 return Err(PoolError::InvalidRelease(
528 "object is not checked out from this pool (double release?)".to_string(),
529 ));
530 }
531 };
532 let now = self.clock.monotonic();
533 let hold = pooled.hold_time(now);
534 let reason = self.check_release(&pooled, &conn, discard, error, now).await;
535 self.events.safe(|l| l.on_released(&pooled, hold));
536 match reason {
537 Some(r) => self.destroy_detached(pooled, r).await,
538 None => {
539 if let Err(p) = self.park(pooled, now) {
540 self.destroy_detached(p, CloseReason::PoolClosed).await;
541 }
542 }
543 }
544 Ok(())
545 }
546
547 async fn check_release(
548 &self,
549 pooled: &PooledConnection<T>,
550 conn: &T,
551 discard: bool,
552 error: Option<&(dyn Error + 'static)>,
553 now: f64,
554 ) -> Option<CloseReason> {
555 if discard {
556 return Some(CloseReason::Discarded);
557 }
558 let error_suspect = match error {
559 Some(e) => {
560 let mut s = self.factory.is_fatal_error(e);
561 if !s {
562 s = !self.is_healthy(conn).await;
563 }
564 s
565 }
566 None => false,
567 };
568 if error_suspect {
569 return Some(CloseReason::Error);
570 }
571 if let Some(r) = rotation_reason(pooled, &self.cfg, now) {
572 return Some(r);
573 }
574 if self.reset_conn(conn).await.is_err() {
575 eprintln!(
576 "connpool: pool {:?}: reset failed; discarding {}",
577 self.name, pooled
578 );
579 return Some(CloseReason::ResetFailed);
580 }
581 None
582 }
583
584 fn park(&self, mut pooled: PooledConnection<T>, now: f64) -> Result<(), PooledConnection<T>> {
585 let mut g = self.state.lock().unwrap();
586 if g.closed {
587 return Err(pooled);
588 }
589 pooled.mark_returned(now);
590 g.idle.push(pooled);
591 self.notify.notify_waiters();
592 Ok(())
593 }
594
595 async fn create(&self, deadline: Option<f64>) -> Result<(PooledConnection<T>, f64), PoolError> {
596 let started = self.clock.monotonic();
597 let raw = self.create_raw(deadline).await?;
598 let now = self.clock.monotonic();
599 Ok((self.new_conn(raw, now), now - started))
600 }
601
602 async fn register_new(
603 &self,
604 pooled: PooledConnection<T>,
605 created_in: f64,
606 ) -> Result<T, PoolError> {
607 let key = pooled.raw.clone();
608 let outcome = {
609 let mut g = self.state.lock().unwrap();
610 if g.closed {
611 None } else if g.checked_out.contains_key(&key) || g.idle.iter().any(|c| c.raw == key) {
613 Some(Err(())) } else {
615 let ev = pooled.clone();
616 g.checked_out.insert(key.clone(), pooled.clone());
617 Some(Ok(ev))
618 }
619 };
620 match outcome {
621 None => {
622 self.events.safe(|l| l.on_connection_created(&pooled, created_in));
623 self.destroy_detached(pooled, CloseReason::PoolClosed).await;
624 Err(PoolError::Closed(format!("pool {:?} is closed", self.name)))
625 }
626 Some(Err(())) => {
627 self.free_slot();
628 Err(PoolError::ConnectionCreate {
629 message: format!(
630 "pool {:?}: factory.create() returned an object the pool already manages; each create must return a distinct connection",
631 self.name
632 ),
633 source: None,
634 })
635 }
636 Some(Ok(ev)) => {
637 self.events.safe(|l| l.on_connection_created(&ev, created_in));
638 Ok(key)
639 }
640 }
641 }
642
643 async fn create_raw(&self, deadline: Option<f64>) -> Result<T, PoolError> {
644 if let Some(b) = self.breaker.get() {
645 b.before_call()?;
646 }
647 match self.create_with_retry(deadline).await {
648 Ok(raw) => {
649 if let Some(b) = self.breaker.get() {
650 b.record_success();
651 }
652 Ok(raw)
653 }
654 Err(e) => {
655 if let Some(b) = self.breaker.get() {
656 if matches!(e, PoolError::ConnectionCreate { .. }) {
657 b.record_failure();
658 } else {
659 b.record_abort();
660 }
661 }
662 Err(e)
663 }
664 }
665 }
666
667 fn create_limit(&self, deadline: Option<f64>) -> Option<f64> {
669 let mut limit: Option<f64> = self.cfg.create_timeout;
670 if let Some(r) = remaining(deadline, self.clock.monotonic()) {
671 limit = Some(match limit {
672 Some(l) => l.min(r),
673 None => r,
674 });
675 }
676 limit.map(|l| l.max(0.0))
677 }
678
679 async fn create_with_retry(&self, deadline: Option<f64>) -> Result<T, PoolError> {
680 let retry = &self.cfg.retry;
681 let mut attempt = 0u32;
682 loop {
683 if self.is_closed() {
684 return Err(PoolError::Closed(format!("pool {:?} is closed", self.name)));
685 }
686 attempt += 1;
687 let limit = self.create_limit(deadline);
688 match self.create_one(limit).await {
689 Ok(raw) => return Ok(raw),
690 Err(CreateErr::Timeout) => {
691 let left = remaining(deadline, self.clock.monotonic());
693 if matches!(left, Some(l) if l <= 0.0) {
694 return Err(PoolError::Timeout(format!(
695 "pool {:?}: acquire deadline reached while creating connection",
696 self.name
697 )));
698 }
699 let e: FactoryError = "create timed out".into();
701 self.events.safe(|l| l.on_create_failed(&*e));
702 if attempt >= retry.max_attempts {
703 return Err(PoolError::ConnectionCreate {
704 message: format!(
705 "pool {:?}: could not create connection after {} attempt(s): create timed out",
706 self.name, attempt
707 ),
708 source: Some(e),
709 });
710 }
711 if let Some(stop) = self.backoff(attempt, deadline).await {
712 return Err(stop);
713 }
714 }
715 Err(CreateErr::Factory(e)) => {
716 self.events.safe(|l| l.on_create_failed(&*e));
717 if attempt >= retry.max_attempts || !retry.should_retry(&*e) {
718 return Err(PoolError::ConnectionCreate {
719 message: format!(
720 "pool {:?}: could not create connection after {} attempt(s): {}",
721 self.name, attempt, e
722 ),
723 source: Some(e),
724 });
725 }
726 if let Some(stop) = self.backoff(attempt, deadline).await {
727 return Err(stop);
728 }
729 }
730 }
731 }
732 }
733
734 async fn backoff(&self, attempt: u32, deadline: Option<f64>) -> Option<PoolError> {
736 let delay = self.retry_delay(attempt);
737 let left = remaining(deadline, self.clock.monotonic());
738 if matches!(left, Some(l) if delay >= l) {
739 return Some(PoolError::Timeout(format!(
740 "pool {:?}: acquire deadline reached while retrying creation",
741 self.name
742 )));
743 }
744 self.clock.sleep_async(delay).await;
745 None
746 }
747
748 async fn create_one(&self, limit: Option<f64>) -> Result<T, CreateErr> {
749 let fut = self.factory.create();
750 let result = match limit {
751 Some(l) => match tokio_timeout(Duration::from_secs_f64(l), fut).await {
752 Ok(r) => r,
753 Err(_) => return Err(CreateErr::Timeout),
754 },
755 None => fut.await,
756 };
757 result.map_err(CreateErr::Factory)
758 }
759
760 async fn is_healthy(&self, raw: &T) -> bool {
761 self.factory.validate(raw).await
762 }
763
764 async fn reset_conn(&self, conn: &T) -> Result<(), FactoryError> {
765 self.factory.reset(conn).await
766 }
767
768 async fn destroy_by_key(&self, key: &T, reason: CloseReason) {
769 let pooled = {
770 let mut g = self.state.lock().unwrap();
771 g.checked_out.remove(key)
772 };
773 let Some(p) = pooled else { return };
774 self.destroy_detached(p, reason).await;
775 }
776
777 async fn destroy_detached(&self, pooled: PooledConnection<T>, reason: CloseReason) {
778 self.close_conn(&pooled).await;
779 {
780 let mut g = self.state.lock().unwrap();
781 g.size -= 1;
782 self.notify.notify_waiters();
783 }
784 self.events.safe(|l| l.on_connection_closed(&pooled, reason));
785 }
786
787 async fn close_conn(&self, pooled: &PooledConnection<T>) {
788 match self.factory.close(&pooled.raw).await {
789 Ok(()) => {}
790 Err(e) => eprintln!(
791 "connpool: pool {:?}: error closing {}: {}",
792 self.name, pooled, e
793 ),
794 }
795 }
796
797 async fn fill_to_min(&self) {
798 loop {
799 {
800 let mut g = self.state.lock().unwrap();
801 if g.closed || g.size >= self.cfg.min_size {
802 return;
803 }
804 g.size += 1;
805 }
806 match self.create(None).await {
807 Err(e) => {
808 self.free_slot();
809 eprintln!("connpool: pool {:?}: warm-up failed: {}", self.name, e);
810 return;
811 }
812 Ok((pooled, created_in)) => {
813 self.events.safe(|l| l.on_connection_created(&pooled, created_in));
814 if let Err(p) = self.park(pooled, self.clock.monotonic()) {
815 self.destroy_detached(p, CloseReason::PoolClosed).await;
816 }
817 }
818 }
819 }
820 }
821
822 async fn run_maintenance(&self) {
823 let doomed = {
824 let mut g = self.state.lock().unwrap();
825 if g.closed {
826 return;
827 }
828 let idle = std::mem::take(&mut g.idle);
829 let (keep, doomed) = sweep(idle, g.size, &self.cfg, self.clock.monotonic());
830 g.idle = keep;
831 doomed
832 };
833 for (p, reason) in doomed {
834 self.destroy_detached(p, reason).await;
835 }
836 self.health_check_idle().await;
837 self.fill_to_min().await;
838 self.check_leaks();
839 }
840
841 async fn run_maintenance_safe(&self) {
842 self.run_maintenance().await;
844 }
845
846 async fn health_check_idle(&self) {
847 if !self.cfg.validate_idle {
848 return;
849 }
850 let keys: Vec<T> = {
851 let mut g = self.state.lock().unwrap();
852 let batch = std::mem::take(&mut g.idle);
853 let mut keys = Vec::with_capacity(batch.len());
854 for conn in batch {
855 let key = conn.raw.clone();
856 g.checked_out.insert(key.clone(), conn);
857 keys.push(key);
858 }
859 keys
860 };
861 for key in keys {
862 let raw = {
863 let g = self.state.lock().unwrap();
864 g.checked_out.get(&key).expect("reserved").raw.clone()
865 };
866 let healthy = self.is_healthy(&raw).await;
867 let doomed = {
868 let mut g = self.state.lock().unwrap();
869 let pooled = g.checked_out.remove(&key).expect("reserved");
870 if healthy && !g.closed {
871 g.idle.push(pooled);
872 self.notify.notify_waiters();
873 None
874 } else {
875 Some(pooled)
876 }
877 };
878 if let Some(p) = doomed {
879 let reason = if healthy {
880 CloseReason::PoolClosed
881 } else {
882 CloseReason::Unhealthy
883 };
884 self.destroy_detached(p, reason).await;
885 }
886 }
887 }
888
889 fn check_leaks(&self) {
890 let timeout = match self.cfg.leak_detection_timeout {
891 Some(t) => t,
892 None => return,
893 };
894 let now = self.clock.monotonic();
895 let leaked: Vec<(PooledConnection<T>, f64)> = {
896 let mut g = self.state.lock().unwrap();
897 let mut out = Vec::new();
898 for pooled in g.checked_out.values_mut() {
899 if !pooled.leak_reported && pooled.hold_time(now) >= timeout {
900 pooled.leak_reported = true;
901 let held = pooled.hold_time(now);
902 out.push((pooled.clone(), held));
903 }
904 }
905 out
906 };
907 for (pooled, held) in leaked {
908 eprintln!(
909 "connpool: pool {:?}: possible connection leak: {} held for {:.1}s",
910 self.name, pooled, held
911 );
912 self.events.safe(|l| l.on_connection_leak(&pooled, held));
913 }
914 }
915
916 fn stats_snapshot(&self) -> PoolStats {
917 let (name, max_size, size, idle, in_use, waiting) = {
918 let g = self.state.lock().unwrap();
919 (
920 self.name.clone(),
921 self.cfg.max_size,
922 g.size,
923 g.idle.len(),
924 g.checked_out.len(),
925 g.waiters,
926 )
927 };
928 let breaker_state = self.breaker.get().map(|b| b.state().as_str().to_string());
929 self.stats.snapshot(StatsGauges {
930 name,
931 max_size,
932 size,
933 idle,
934 in_use,
935 waiting,
936 breaker_state,
937 })
938 }
939}
940
941enum CreateErr {
944 Timeout,
945 Factory(FactoryError),
946}
947
948struct WaiterGuard<'a, T, F> {
950 inner: &'a Inner<T, F>,
951}
952
953impl<T, F> Drop for WaiterGuard<'_, T, F> {
954 fn drop(&mut self) {
955 let mut g = self.inner.state.lock().unwrap();
956 g.waiters = g.waiters.saturating_sub(1);
957 }
958}
959
960#[cfg(test)]
961mod tests {
962 use super::*;
963 use crate::async_test_support::*;
964 use crate::factories::async_factory::AsyncFnFactory;
965 use crate::policy::config::IdleStrategy;
966 use crate::resilience::breaker::BreakerConfig;
967 use crate::resilience::retry::RetryPolicy;
968 use std::sync::atomic::Ordering;
969
970 fn plain(max_size: usize) -> PoolConfig {
971 PoolConfig {
972 max_size,
973 maintenance_interval: None,
974 breaker: None,
975 ..Default::default()
976 }
977 }
978
979 #[tokio::test]
980 async fn acquire_release_reuse_and_timeout() {
981 let h = AsyncHarness::new();
982 let p = h.pool(plain(1));
983 let a = p.acquire(Timeout::Unset).await.unwrap();
984 assert!(matches!(
985 p.acquire(Timeout::Of(0.0)).await,
986 Err(PoolError::Timeout(_))
987 ));
988 p.release(a).await.unwrap();
989 let b = p.acquire(Timeout::Unset).await.unwrap();
990 assert_eq!(b.id, a.id);
991 p.release(b).await.unwrap();
992 let s = p.stats();
993 assert_eq!(s.created, 1);
994 assert_eq!(s.acquired, 2);
995 assert_eq!(s.timeouts, 1);
996 assert_eq!(p.name(), "test");
997 p.close(None).await;
998 p.close(None).await; assert!(p.is_closed());
1000 assert!(matches!(
1001 p.acquire(Timeout::Unset).await,
1002 Err(PoolError::Closed(_))
1003 ));
1004 }
1005
1006 #[tokio::test]
1007 async fn fifo_strategy_and_config_validation() {
1008 let h = AsyncHarness::new();
1009 let p = h.pool(PoolConfig {
1010 idle_strategy: IdleStrategy::Fifo,
1011 ..plain(3)
1012 });
1013 let a = p.acquire(Timeout::Unset).await.unwrap();
1014 let b = p.acquire(Timeout::Unset).await.unwrap();
1015 p.release(a).await.unwrap();
1016 p.release(b).await.unwrap();
1017 let c = p.acquire(Timeout::Unset).await.unwrap();
1018 assert_eq!(c.id, a.id);
1019 p.release(c).await.unwrap();
1020
1021 let bad = AsyncConnectionPool::new(
1022 h.factory(),
1023 PoolConfig { max_size: 0, ..Default::default() },
1024 None,
1025 );
1026 assert!(matches!(bad, Err(PoolError::Config(_))));
1027 }
1028
1029 #[tokio::test]
1030 async fn invalid_release_detected() {
1031 let h = AsyncHarness::new();
1032 let p = h.pool(plain(2));
1033 assert!(matches!(
1034 p.release(Conn::new(999)).await,
1035 Err(PoolError::InvalidRelease(_))
1036 ));
1037 }
1038
1039 #[tokio::test]
1040 async fn max_waiters_backpressure() {
1041 let h = AsyncHarness::new();
1042 let p = h.pool(PoolConfig { max_waiters: Some(0), ..plain(1) });
1043 let a = p.acquire(Timeout::Unset).await.unwrap();
1044 assert!(matches!(
1045 p.acquire(Timeout::Infinite).await,
1046 Err(PoolError::Exhausted(_))
1047 ));
1048 p.release(a).await.unwrap();
1049 }
1050
1051 #[tokio::test]
1052 async fn blocked_waiter_woken_on_release() {
1053 let h = AsyncHarness::new();
1054 let p = h.pool(plain(1));
1055 let a = p.acquire(Timeout::Unset).await.unwrap();
1056 let p2 = p.clone();
1057 let waiter = tokio::spawn(async move { p2.acquire(Timeout::Infinite).await.map(|c| c.id) });
1058 tokio::time::sleep(Duration::from_millis(30)).await;
1059 p.release(a).await.unwrap();
1060 let got = waiter.await.unwrap().unwrap();
1061 assert_eq!(got, a.id);
1062 p.release(Conn::new(got)).await.unwrap();
1063 }
1064
1065 #[tokio::test]
1066 async fn cancelled_waiter_does_not_leak_waiter_slot() {
1067 let h = AsyncHarness::new();
1068 let p = h.pool(plain(1));
1069 let a = p.acquire(Timeout::Unset).await.unwrap();
1070 {
1071 let fut = p.acquire(Timeout::Infinite);
1073 tokio::pin!(fut);
1074 let _ = tokio::time::timeout(Duration::from_millis(20), &mut fut).await;
1075 }
1077 assert_eq!(p.stats().waiting, 0);
1078 p.release(a).await.unwrap();
1079 }
1080
1081 #[tokio::test]
1082 async fn real_time_wait_times_out() {
1083 let h = AsyncHarness::with_system_clock();
1085 let p = h.pool(plain(1));
1086 let a = p.acquire(Timeout::Unset).await.unwrap();
1087 assert!(matches!(
1088 p.acquire(Timeout::Of(0.05)).await,
1089 Err(PoolError::Timeout(_))
1090 ));
1091 p.release(a).await.unwrap();
1092 }
1093
1094 #[tokio::test]
1095 async fn discard_error_and_reset_paths() {
1096 let h = AsyncHarness::new();
1097 let p = h.pool(plain(5));
1098 let a = p.acquire(Timeout::Unset).await.unwrap();
1099 p.discard(a).await.unwrap();
1100 assert!(h.fac.closed_ids().contains(&a.id));
1101
1102 h.fac.set_validate_ok(false);
1103 let b = p.acquire(Timeout::Unset).await.unwrap();
1104 let e: FactoryError = "bad".into();
1105 p.release_with_error(b, &*e).await.unwrap();
1106 assert!(h.fac.closed_ids().contains(&b.id));
1107
1108 h.fac.set_validate_ok(true);
1109 h.fac.set_reset_ok(false);
1110 let c = p.acquire(Timeout::Unset).await.unwrap();
1111 p.release(c).await.unwrap();
1112 assert_eq!(p.stats().closed_by_reason.get("reset_failed"), Some(&1));
1113 }
1114
1115 #[tokio::test]
1116 async fn fatal_error_closes_without_validation() {
1117 let h = AsyncHarness::new();
1118 h.fac.set_fatal(true);
1119 let p = h.pool(plain(5));
1120 let a = p.acquire(Timeout::Unset).await.unwrap();
1121 let e: FactoryError = "fatal".into();
1122 p.release_with_error(a, &*e).await.unwrap();
1123 assert!(h.fac.closed_ids().contains(&a.id));
1124 }
1125
1126 #[tokio::test]
1127 async fn rotation_and_stale_replacement() {
1128 let h = AsyncHarness::new();
1129 let p = h.pool(PoolConfig {
1130 max_lifetime: Some(100.0),
1131 lifetime_jitter: 0.0,
1132 max_idle_time: None,
1133 validate_after_idle: None,
1134 ..plain(5)
1135 });
1136 let a = p.acquire(Timeout::Unset).await.unwrap();
1137 p.release(a).await.unwrap();
1138 h.clock.advance(200.0);
1139 let b = p.acquire(Timeout::Unset).await.unwrap();
1140 assert_ne!(b.id, a.id);
1141 p.release(b).await.unwrap();
1142 }
1143
1144 #[tokio::test]
1145 async fn validate_on_checkout_replaces_unhealthy() {
1146 let h = AsyncHarness::new();
1147 let p = h.pool(PoolConfig {
1148 validate_after_idle: Some(5.0),
1149 max_lifetime: None,
1150 max_idle_time: None,
1151 ..plain(5)
1152 });
1153 let a = p.acquire(Timeout::Unset).await.unwrap();
1154 p.release(a).await.unwrap();
1155 h.clock.advance(10.0);
1156 h.fac.set_validate_ok(false);
1157 let b = p.acquire(Timeout::Unset).await.unwrap();
1158 assert_ne!(b.id, a.id);
1159 assert_eq!(p.stats().closed_by_reason.get("unhealthy"), Some(&1));
1160 p.release(b).await.unwrap();
1161 }
1162
1163 #[tokio::test]
1164 async fn create_failure_retry_then_error() {
1165 let h = AsyncHarness::new();
1166 h.fac.fail_next_creates(10);
1167 let p = h.pool(PoolConfig {
1168 retry: RetryPolicy { max_attempts: 3, base_delay: 0.0, ..Default::default() },
1169 ..plain(2)
1170 });
1171 assert!(matches!(
1172 p.acquire(Timeout::Unset).await,
1173 Err(PoolError::ConnectionCreate { .. })
1174 ));
1175 assert_eq!(p.stats().create_failures, 3);
1176 h.fac.fail_next_creates(1);
1178 let a = p.acquire(Timeout::Unset).await.unwrap(); p.release(a).await.unwrap();
1180 }
1181
1182 #[tokio::test]
1183 async fn create_timeout_counts_as_backend_failure() {
1184 let h = AsyncHarness::with_system_clock();
1185 h.fac.set_create_delay_ms(1000); let p = h.pool(PoolConfig {
1187 create_timeout: Some(0.02),
1188 acquire_timeout: Some(5.0),
1189 retry: RetryPolicy::no_retry(),
1190 ..plain(2)
1191 });
1192 let err = p.acquire(Timeout::Unset).await.unwrap_err();
1193 assert!(matches!(err, PoolError::ConnectionCreate { .. }));
1194 assert!(p.stats().create_failures >= 1);
1195 }
1196
1197 #[tokio::test]
1198 async fn retry_backoff_exceeding_deadline_times_out() {
1199 let h = AsyncHarness::new();
1200 h.fac.fail_next_creates(10);
1201 let p = h.pool(PoolConfig {
1202 acquire_timeout: Some(0.05),
1203 retry: RetryPolicy { max_attempts: 5, base_delay: 100.0, jitter: 0.0, ..Default::default() },
1204 ..plain(2)
1205 });
1206 assert!(matches!(
1207 p.acquire(Timeout::Unset).await,
1208 Err(PoolError::Timeout(_))
1209 ));
1210 }
1211
1212 #[tokio::test]
1213 async fn breaker_trips_blocks_recovers_and_abort_is_safe() {
1214 let h = AsyncHarness::new();
1215 h.fac.fail_next_creates(100);
1216 let p = h.pool(PoolConfig {
1217 retry: RetryPolicy::no_retry(),
1218 maintenance_interval: None,
1219 breaker: Some(BreakerConfig {
1220 failure_threshold: 1,
1221 recovery_timeout: 10.0,
1222 half_open_max_calls: 1,
1223 success_threshold: 1,
1224 }),
1225 ..Default::default()
1226 });
1227 assert!(matches!(
1228 p.acquire(Timeout::Unset).await,
1229 Err(PoolError::ConnectionCreate { .. })
1230 ));
1231 assert!(matches!(
1232 p.acquire(Timeout::Unset).await,
1233 Err(PoolError::CircuitOpen { .. })
1234 ));
1235 assert_eq!(p.stats().breaker_state.as_deref(), Some("open"));
1236 h.clock.advance(10.0);
1237 h.fac.fail_next_creates(0);
1238 let a = p.acquire(Timeout::Unset).await.unwrap();
1239 assert_eq!(p.stats().breaker_state.as_deref(), Some("closed"));
1240 p.release(a).await.unwrap();
1241 }
1242
1243 #[tokio::test]
1244 async fn duplicate_object_rejected() {
1245 let h = AsyncHarness::new();
1246 h.fac.force_id(Some(42));
1247 let p = h.pool(plain(5));
1248 let _a = p.acquire(Timeout::Unset).await.unwrap();
1249 assert!(matches!(
1250 p.acquire(Timeout::Unset).await,
1251 Err(PoolError::ConnectionCreate { .. })
1252 ));
1253 }
1254
1255 #[tokio::test]
1256 async fn open_warms_up_close_drains_and_maintenance() {
1257 let h = AsyncHarness::new();
1258 let rec = Recorder::new();
1259 let p = h.pool_with(
1260 PoolConfig {
1261 min_size: 2,
1262 max_lifetime: Some(100.0),
1263 lifetime_jitter: 0.0,
1264 max_idle_time: Some(50.0),
1265 validate_after_idle: None,
1266 maintenance_interval: None,
1267 breaker: None,
1268 ..Default::default()
1269 },
1270 vec![rec.clone()],
1271 );
1272 p.open().await.unwrap();
1273 p.open().await.unwrap(); assert_eq!(p.stats().idle, 2);
1275 h.clock.advance(200.0);
1277 p.run_maintenance().await;
1278 assert_eq!(p.stats().idle, 2);
1279 assert!(p.stats().closed >= 2);
1280 p.close(Some(0.0)).await;
1281 assert!(matches!(p.open().await, Err(PoolError::Closed(_))));
1282 }
1283
1284 #[tokio::test]
1285 async fn maintenance_noop_when_closed() {
1286 let h = AsyncHarness::new();
1287 let p = h.pool(plain(2));
1288 p.close(None).await;
1289 p.run_maintenance().await;
1290 }
1291
1292 #[tokio::test]
1293 async fn health_check_idle_and_leak_detection() {
1294 let h = AsyncHarness::new();
1295 let rec = Recorder::new();
1296 let p = h.pool_with(
1297 PoolConfig {
1298 validate_idle: true,
1299 leak_detection_timeout: Some(10.0),
1300 max_lifetime: None,
1301 max_idle_time: None,
1302 validate_after_idle: None,
1303 maintenance_interval: None,
1304 breaker: None,
1305 ..Default::default()
1306 },
1307 vec![rec.clone()],
1308 );
1309 let a = p.acquire(Timeout::Unset).await.unwrap();
1310 h.clock.advance(20.0);
1311 p.run_maintenance().await; p.run_maintenance().await; assert_eq!(rec.leaks.load(Ordering::SeqCst), 1);
1314 p.release(a).await.unwrap();
1315 p.run_maintenance().await;
1317 assert_eq!(p.stats().idle, 1);
1318 h.fac.set_validate_ok(false);
1319 p.run_maintenance().await;
1320 assert_eq!(p.stats().idle, 0);
1321 }
1322
1323 #[tokio::test]
1324 async fn release_into_closed_pool_closes_connection() {
1325 let h = AsyncHarness::new();
1326 let p = h.pool(plain(2));
1327 let a = p.acquire(Timeout::Unset).await.unwrap();
1328 p.close(None).await;
1329 p.release(a).await.unwrap();
1330 assert!(h.fac.closed_ids().contains(&a.id));
1331 }
1332
1333 #[tokio::test]
1334 async fn close_drains_checked_out_with_timeout() {
1335 let h = AsyncHarness::new();
1336 let p = h.pool(plain(2));
1337 let a = p.acquire(Timeout::Unset).await.unwrap();
1338 let p2 = p.clone();
1339 let closer = tokio::spawn(async move { p2.close(Some(5.0)).await });
1340 tokio::time::sleep(Duration::from_millis(30)).await;
1341 p.release(a).await.unwrap();
1342 closer.await.unwrap();
1343 assert!(p.is_closed());
1344 }
1345
1346 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
1347 async fn background_maintenance_task_runs_and_stops() {
1348 let h = AsyncHarness::with_system_clock();
1349 let p = h.pool(PoolConfig {
1350 maintenance_interval: Some(0.01),
1351 breaker: None,
1352 ..Default::default()
1353 });
1354 p.open().await.unwrap();
1355 tokio::time::sleep(Duration::from_millis(40)).await;
1356 p.close(None).await;
1357 assert!(p.is_closed());
1358 }
1359
1360 #[tokio::test]
1361 async fn default_options_and_warmup_failure() {
1362 let h = AsyncHarness::new();
1363 let p = AsyncConnectionPool::new(h.factory(), plain(2), None).unwrap();
1365 assert_eq!(p.name(), "pool");
1366 let a = p.acquire(Timeout::Unset).await.unwrap();
1367 p.release(a).await.unwrap();
1368
1369 let h2 = AsyncHarness::new();
1371 h2.fac.fail_next_creates(100);
1372 let p2 = h2.pool(PoolConfig {
1373 min_size: 1,
1374 retry: RetryPolicy::no_retry(),
1375 maintenance_interval: None,
1376 breaker: None,
1377 ..Default::default()
1378 });
1379 p2.open().await.unwrap(); assert_eq!(p2.stats().idle, 0);
1381 }
1382
1383 #[tokio::test]
1384 async fn cancelled_create_frees_slot() {
1385 let h = AsyncHarness::with_system_clock();
1386 h.fac.set_create_delay_ms(1000);
1387 let p = h.pool(plain(1));
1388 {
1389 let fut = p.acquire(Timeout::Infinite);
1390 tokio::pin!(fut);
1391 let _ = tokio::time::timeout(Duration::from_millis(20), &mut fut).await;
1392 }
1394 tokio::time::sleep(Duration::from_millis(5)).await;
1395 assert_eq!(p.stats().size, 0);
1396 h.fac.set_create_delay_ms(0);
1398 let a = p.acquire(Timeout::Unset).await.unwrap();
1399 p.release(a).await.unwrap();
1400 }
1401
1402 #[tokio::test]
1403 async fn cancelled_borrow_returns_connection() {
1404 let h = AsyncHarness::with_system_clock();
1405 let p = h.pool(PoolConfig {
1406 validate_after_idle: Some(0.0), max_lifetime: None,
1408 max_idle_time: None,
1409 ..plain(1)
1410 });
1411 let a = p.acquire(Timeout::Unset).await.unwrap();
1413 p.release(a).await.unwrap();
1414 h.fac.set_validate_delay_ms(1000); {
1416 let fut = p.acquire(Timeout::Infinite);
1417 tokio::pin!(fut);
1418 let _ = tokio::time::timeout(Duration::from_millis(20), &mut fut).await;
1419 }
1421 tokio::time::sleep(Duration::from_millis(5)).await;
1422 assert_eq!(p.stats().idle, 1);
1423 assert_eq!(p.stats().in_use, 0);
1424 }
1425
1426 #[tokio::test]
1427 async fn listener_panic_does_not_break_pool() {
1428 struct Boom;
1429 impl PoolListener<Conn> for Boom {
1430 fn on_acquired(&self, _c: &PooledConnection<Conn>, _w: f64) {
1431 panic!("boom");
1432 }
1433 }
1434 let h = AsyncHarness::new();
1435 let p = h.pool_with(plain(2), vec![Arc::new(Boom)]);
1436 let a = p.acquire(Timeout::Unset).await.unwrap();
1437 p.release(a).await.unwrap();
1438 }
1439
1440 #[tokio::test]
1441 async fn waiter_under_max_waiters_blocks_then_succeeds() {
1442 let h = AsyncHarness::new();
1443 let p = h.pool(PoolConfig { max_waiters: Some(5), ..plain(1) });
1444 let a = p.acquire(Timeout::Unset).await.unwrap();
1445 let p2 = p.clone();
1446 let waiter = tokio::spawn(async move { p2.acquire(Timeout::Infinite).await.map(|c| c.id) });
1447 tokio::time::sleep(Duration::from_millis(30)).await;
1448 p.release(a).await.unwrap();
1449 assert_eq!(waiter.await.unwrap().unwrap(), a.id);
1450 p.release(Conn::new(a.id)).await.unwrap();
1451 }
1452
1453 #[tokio::test]
1454 async fn create_timeout_then_backoff_hits_deadline() {
1455 let h = AsyncHarness::with_system_clock();
1456 h.fac.set_create_delay_ms(1000);
1457 let p = h.pool(PoolConfig {
1458 acquire_timeout: Some(1.0),
1459 create_timeout: Some(0.01),
1460 retry: RetryPolicy { max_attempts: 5, base_delay: 100.0, jitter: 0.0, ..Default::default() },
1461 ..plain(2)
1462 });
1463 assert!(matches!(p.acquire(Timeout::Unset).await, Err(PoolError::Timeout(_))));
1466 }
1467
1468 #[tokio::test]
1469 async fn rotation_on_release() {
1470 let h = AsyncHarness::new();
1471 let p = h.pool(PoolConfig { max_uses: Some(1), max_lifetime: None, ..plain(5) });
1472 let a = p.acquire(Timeout::Unset).await.unwrap();
1473 p.release(a).await.unwrap(); assert_eq!(p.stats().closed_by_reason.get("max_uses"), Some(&1));
1475 }
1476
1477 #[tokio::test]
1478 async fn breaker_abort_does_not_trip() {
1479 let h = AsyncHarness::new();
1480 h.fac.fail_next_creates(100);
1481 let p = h.pool(PoolConfig {
1482 acquire_timeout: Some(0.05),
1483 retry: RetryPolicy { max_attempts: 5, base_delay: 100.0, jitter: 0.0, ..Default::default() },
1484 maintenance_interval: None,
1485 breaker: Some(BreakerConfig { failure_threshold: 1, ..Default::default() }),
1486 ..Default::default()
1487 });
1488 assert!(matches!(p.acquire(Timeout::Unset).await, Err(PoolError::Timeout(_))));
1489 assert_eq!(p.stats().breaker_state.as_deref(), Some("closed"));
1490 }
1491
1492 #[tokio::test]
1493 async fn create_limit_from_remaining_deadline() {
1494 let h = AsyncHarness::with_system_clock();
1496 let p = h.pool(PoolConfig { create_timeout: None, acquire_timeout: Some(5.0), ..plain(2) });
1497 let a = p.acquire(Timeout::Of(5.0)).await.unwrap();
1498 p.release(a).await.unwrap();
1499 }
1500
1501 #[tokio::test]
1502 async fn create_with_no_limit_at_all() {
1503 let h = AsyncHarness::with_system_clock();
1505 let p = h.pool(PoolConfig { create_timeout: None, ..plain(2) });
1506 let a = p.acquire(Timeout::Infinite).await.unwrap();
1507 p.release(a).await.unwrap();
1508 }
1509
1510 #[tokio::test]
1511 async fn create_timeout_bound_by_acquire_deadline_is_timeout() {
1512 let h = AsyncHarness::with_system_clock();
1513 h.fac.set_create_delay_ms(1000);
1514 let p = h.pool(PoolConfig {
1515 acquire_timeout: Some(0.02),
1516 create_timeout: Some(10.0),
1517 retry: RetryPolicy::no_retry(),
1518 ..plain(2)
1519 });
1520 assert!(matches!(p.acquire(Timeout::Unset).await, Err(PoolError::Timeout(_))));
1522 }
1523
1524 #[tokio::test]
1525 async fn create_timeout_then_backoff_then_fail() {
1526 let h = AsyncHarness::with_system_clock();
1527 h.fac.set_create_delay_ms(1000);
1528 let p = h.pool(PoolConfig {
1529 acquire_timeout: Some(5.0),
1530 create_timeout: Some(0.02),
1531 retry: RetryPolicy { max_attempts: 2, base_delay: 0.0, ..Default::default() },
1532 ..plain(2)
1533 });
1534 assert!(matches!(
1536 p.acquire(Timeout::Unset).await,
1537 Err(PoolError::ConnectionCreate { .. })
1538 ));
1539 assert!(p.stats().create_failures >= 2);
1540 }
1541
1542 #[tokio::test]
1543 async fn close_error_is_logged() {
1544 let h = AsyncHarness::new();
1545 h.fac.set_close_err(true);
1546 let p = h.pool(plain(5));
1547 let a = p.acquire(Timeout::Unset).await.unwrap();
1548 p.discard(a).await.unwrap(); assert_eq!(p.stats().closed, 1);
1550 }
1551
1552 #[tokio::test]
1553 async fn close_drain_gives_up_at_deadline() {
1554 let h = AsyncHarness::new();
1555 let p = h.pool(plain(2));
1556 let a = p.acquire(Timeout::Unset).await.unwrap();
1557 p.close(Some(0.0)).await; assert!(p.is_closed());
1559 p.release(a).await.unwrap();
1560 }
1561
1562 #[tokio::test]
1563 async fn blocked_waiter_wakes_as_closed() {
1564 let h = AsyncHarness::new();
1565 let p = h.pool(plain(1));
1566 let a = p.acquire(Timeout::Unset).await.unwrap();
1567 let p2 = p.clone();
1568 let waiter = tokio::spawn(async move { p2.acquire(Timeout::Infinite).await });
1569 tokio::time::sleep(Duration::from_millis(30)).await;
1570 p.close(None).await;
1571 assert!(matches!(waiter.await.unwrap(), Err(PoolError::Closed(_))));
1572 p.release(a).await.unwrap();
1573 }
1574
1575 #[tokio::test(flavor = "multi_thread", worker_threads = 2)]
1576 async fn maintenance_task_stops_when_pool_dropped() {
1577 let h = AsyncHarness::with_system_clock();
1578 let p = h.pool(PoolConfig {
1579 maintenance_interval: Some(0.01),
1580 breaker: None,
1581 ..Default::default()
1582 });
1583 p.open().await.unwrap();
1584 drop(p); tokio::time::sleep(Duration::from_millis(50)).await;
1586 }
1587
1588 #[tokio::test]
1589 async fn cancelled_borrow_while_closed_frees_slot() {
1590 let h = AsyncHarness::with_system_clock();
1591 let p = h.pool(PoolConfig {
1592 validate_after_idle: Some(0.0),
1593 max_lifetime: None,
1594 max_idle_time: None,
1595 ..plain(1)
1596 });
1597 let a = p.acquire(Timeout::Unset).await.unwrap();
1598 p.release(a).await.unwrap(); h.fac.set_validate_delay_ms(1000);
1600 {
1601 let fut = p.acquire(Timeout::Infinite);
1602 tokio::pin!(fut);
1603 let _ = tokio::time::timeout(Duration::from_millis(20), &mut fut).await;
1604 p.close(None).await; }
1607 tokio::time::sleep(Duration::from_millis(5)).await;
1608 assert_eq!(p.stats().size, 0);
1609 }
1610
1611 type SelfClosePool = AsyncConnectionPool<Conn, AsyncFnFactory<Conn>>;
1614
1615 fn self_closing_create(ok: bool) -> (SelfClosePool, PoolConfig) {
1616 let cfg = PoolConfig {
1617 min_size: 0,
1618 retry: RetryPolicy { max_attempts: 2, base_delay: 0.0, ..Default::default() },
1619 maintenance_interval: None,
1620 breaker: None,
1621 ..Default::default()
1622 };
1623 (build_self_closing(cfg.clone(), ok, false), cfg)
1624 }
1625
1626 fn build_self_closing(cfg: PoolConfig, create_ok: bool, close_on_validate: bool) -> SelfClosePool {
1627 use std::sync::Mutex as StdMutex;
1628 let slot: Arc<StdMutex<Option<SelfClosePool>>> = Arc::new(StdMutex::new(None));
1629 let s_create = slot.clone();
1630 let s_validate = slot.clone();
1631 let mut factory = AsyncFnFactory::new(move || {
1632 let s = s_create.clone();
1633 Box::pin(async move {
1634 if !close_on_validate {
1635 let p = s.lock().unwrap().clone().unwrap();
1636 p.close(None).await;
1637 }
1638 if create_ok {
1639 Ok::<_, FactoryError>(Conn::new(7))
1640 } else {
1641 Err::<Conn, FactoryError>("boom".into())
1642 }
1643 }) as _
1644 });
1645 if close_on_validate {
1649 factory = factory.with_validate(move |_c: &Conn| {
1650 let s = s_validate.clone();
1651 Box::pin(async move {
1652 let p = s.lock().unwrap().clone().unwrap();
1653 p.close(None).await;
1654 true
1655 }) as _
1656 });
1657 }
1658 let pool = AsyncConnectionPool::new(factory, cfg, None).unwrap();
1659 *slot.lock().unwrap() = Some(pool.clone());
1660 pool
1661 }
1662
1663 #[tokio::test]
1664 async fn pool_closed_during_create_registers_as_closed() {
1665 let (pool, _cfg) = self_closing_create(true);
1666 assert!(matches!(
1667 pool.acquire(Timeout::Unset).await,
1668 Err(PoolError::Closed(_))
1669 ));
1670 }
1671
1672 #[tokio::test]
1673 async fn pool_closed_mid_retry_loop() {
1674 let (pool, _cfg) = self_closing_create(false);
1675 assert!(matches!(
1676 pool.acquire(Timeout::Unset).await,
1677 Err(PoolError::Closed(_))
1678 ));
1679 }
1680
1681 #[tokio::test]
1682 async fn fill_to_min_park_fails_when_closed_mid_create() {
1683 let cfg = PoolConfig {
1684 min_size: 1,
1685 retry: RetryPolicy::no_retry(),
1686 maintenance_interval: None,
1687 breaker: None,
1688 ..Default::default()
1689 };
1690 let pool = build_self_closing(cfg, true, false);
1691 pool.open().await.unwrap(); assert!(pool.is_closed());
1693 }
1694
1695 #[tokio::test]
1696 async fn health_check_closed_mid_validation() {
1697 let cfg = PoolConfig {
1698 validate_idle: true,
1699 validate_after_idle: None,
1700 max_lifetime: None,
1701 max_idle_time: None,
1702 maintenance_interval: None,
1703 breaker: None,
1704 ..Default::default()
1705 };
1706 let pool = build_self_closing(cfg, true, true);
1707 let a = pool.acquire(Timeout::Unset).await.unwrap();
1708 pool.release(a).await.unwrap(); pool.run_maintenance().await; assert!(pool.is_closed());
1711 }
1712}