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