1#[cfg(feature = "opentelemetry")]
8use super::metrics;
9use super::{ActiveSlot, ConnID, ConnectionResult, Pool, PoolSlot};
10use crate::address::SocketAddress;
11use crate::conn::{ConnectionHealth, ConnectionHealthWatcher};
12use crate::stream::Socket;
13use parking_lot::Mutex;
14use rama_core::Service;
15use rama_core::error::BoxErrorExt as _;
16use rama_core::error::{BoxError, ErrorContext, ErrorExt};
17use rama_core::extensions::{Extension, Extensions, ExtensionsRef};
18use rama_core::telemetry::tracing::trace;
19use rama_utils::macros::generate_set_and_with;
20use std::collections::VecDeque;
21use std::fmt::Debug;
22use std::mem::ManuallyDrop;
23use std::ops::{Deref, DerefMut};
24use std::pin::Pin;
25use std::sync::atomic::{AtomicBool, Ordering};
26use std::sync::{Arc, Weak};
27use std::time::{Duration, Instant};
28use tokio::io::{AsyncRead, AsyncWrite};
29use tokio::sync::Semaphore;
30
31pub struct LeasedConnection<C: ExtensionsRef, ID> {
38 pooled_conn: ManuallyDrop<PooledConnection<C, ID>>,
39 pooled_conn_taken: bool,
40 active_slot: ActiveSlot,
41 returner: ConnReturner<C, ID>,
42 got_response: AtomicBool,
43 drop_connection_if_no_response: bool,
44}
45
46impl<C: ExtensionsRef, ID> LeasedConnection<C, ID> {
47 pub fn into_connection(mut self) -> C {
48 self.pooled_conn_taken = true;
51 unsafe { ManuallyDrop::take(&mut self.pooled_conn) }.conn
54 }
55}
56
57impl<C: ExtensionsRef, ID> ExtensionsRef for LeasedConnection<C, ID> {
58 fn extensions(&self) -> &Extensions {
59 self.pooled_conn.extensions()
60 }
61}
62
63struct PooledConnection<C, ID> {
68 conn: C,
69 id: ID,
70 pool_slot: PoolSlot,
71 last_used: Instant,
72}
73
74impl<C: ExtensionsRef, ID> ExtensionsRef for PooledConnection<C, ID> {
75 fn extensions(&self) -> &Extensions {
76 self.conn.extensions()
77 }
78}
79
80pub struct LruDropPool<C, ID> {
82 storage: Arc<Mutex<VecDeque<PooledConnection<C, ID>>>>,
83 total_slots: Arc<Semaphore>,
84 active_slots: Arc<Semaphore>,
85 idle_timeout: Option<Duration>,
86 returner: ConnReturner<C, ID>,
87 reuse_strategy: ReuseStrategy,
88 drop_connection_if_no_response: bool,
89 #[cfg(feature = "opentelemetry")]
90 metrics: Option<Arc<metrics::PoolMetrics>>,
91}
92
93#[non_exhaustive]
94#[derive(Clone, Copy, Debug, Default)]
95pub enum ReuseStrategy {
96 #[default]
97 FiFo,
98 RoundRobin,
99}
100
101struct ConnReturner<C, ID> {
102 weak_storage: Weak<Mutex<VecDeque<PooledConnection<C, ID>>>>,
103}
104
105impl<C, ID> Clone for ConnReturner<C, ID> {
106 fn clone(&self) -> Self {
107 Self {
108 weak_storage: self.weak_storage.clone(),
109 }
110 }
111}
112
113impl<C, ID> ConnReturner<C, ID> {
114 fn return_conn(&self, mut conn: PooledConnection<C, ID>) {
115 if let Some(storage) = self.weak_storage.upgrade() {
116 let mut storage = storage.lock();
119 conn.last_used = Instant::now();
120 storage.push_front(conn);
121 }
122 }
123}
124
125impl<C, ID> Clone for LruDropPool<C, ID> {
126 fn clone(&self) -> Self {
127 Self {
128 storage: self.storage.clone(),
129 total_slots: self.total_slots.clone(),
130 active_slots: self.active_slots.clone(),
131 returner: self.returner.clone(),
132 idle_timeout: self.idle_timeout,
133 reuse_strategy: self.reuse_strategy,
134 drop_connection_if_no_response: self.drop_connection_if_no_response,
135 #[cfg(feature = "opentelemetry")]
136 metrics: self.metrics.clone(),
137 }
138 }
139}
140
141impl<C, ID> LruDropPool<C, ID> {
142 pub fn try_new(max_active: usize, max_total: usize) -> Result<Self, BoxError> {
143 if max_active == 0 || max_total == 0 {
144 return Err(BoxError::from_static_str(
145 "max_active or max_total of 0 will make this pool unusable",
146 )
147 .context_field("max_active", max_active)
148 .context_field("max_total", max_total));
149 }
150 if max_active > max_total {
151 return Err(BoxError::from_static_str(
152 "max_active should be smaller or equal to max_total",
153 )
154 .context_field("max_active", max_active)
155 .context_field("max_total", max_total));
156 }
157 let storage = Arc::new(Mutex::new(VecDeque::with_capacity(max_total)));
158 let weak_storage = Arc::downgrade(&storage);
159 Ok(Self {
160 storage,
161 returner: ConnReturner { weak_storage },
162 total_slots: Arc::new(Semaphore::const_new(max_total)),
163 active_slots: Arc::new(Semaphore::const_new(max_active)),
164 idle_timeout: None,
165 reuse_strategy: ReuseStrategy::default(),
166 drop_connection_if_no_response: true,
167 #[cfg(feature = "opentelemetry")]
168 metrics: None,
169 })
170 }
171
172 generate_set_and_with! {
173 pub fn idle_timeout(mut self, timeout: Option<Duration>) -> Self {
179 self.idle_timeout = timeout;
180 self
181 }
182 }
183
184 generate_set_and_with! {
185 pub fn reuse_strategy(mut self, strategy: ReuseStrategy) -> Self {
186 self.reuse_strategy = strategy;
187 self
188 }
189 }
190
191 generate_set_and_with! {
192 pub fn drop_connection_if_no_response(mut self, drop_connection_if_no_response: bool) -> Self {
197 self.drop_connection_if_no_response = drop_connection_if_no_response;
198 self
199 }
200 }
201
202 #[cfg(feature = "opentelemetry")]
203 generate_set_and_with! {
204 #[cfg_attr(docsrs, doc(cfg(feature = "opentelemetry")))]
205 pub fn metrics(mut self, metrics: Option<Arc<metrics::PoolMetrics>>) -> Self {
206 self.metrics = metrics;
207 self
208 }
209 }
210}
211
212impl<C, ID> Pool<C, ID> for LruDropPool<C, ID>
213where
214 C: Send + ExtensionsRef + 'static,
215 ID: ConnID,
216{
217 type Connection = LeasedConnection<C, ID>;
218 type CreatePermit = (ActiveSlot, PoolSlot);
219
220 async fn get_conn(
221 &self,
222 id: &ID,
223 ) -> Result<ConnectionResult<Self::Connection, Self::CreatePermit>, BoxError> {
224 #[cfg(feature = "opentelemetry")]
225 let metrics = self
226 .metrics
227 .as_ref()
228 .map(|metrics| (metrics, metrics.attributes(id)));
229
230 #[cfg(feature = "opentelemetry")]
231 let start = Instant::now();
232 let active_slot = ActiveSlot(
233 self.active_slots
234 .clone()
235 .acquire_owned()
236 .await
237 .context("get active pool slot")?,
238 );
239
240 #[cfg(feature = "opentelemetry")]
241 if let Some((metrics, metric_attrs)) = &metrics {
242 let active_connection_delay_nanoseconds = start.elapsed().as_nanos() as f64;
243 metrics
244 .active_connection_delay_nanoseconds
245 .record(active_connection_delay_nanoseconds, metric_attrs);
246 };
247
248 let mut storage = self.storage.lock();
249
250 if let Some(timeout) = self.idle_timeout {
251 let now = Instant::now();
261 let idx = storage.partition_point(|conn| now.duration_since(conn.last_used) <= timeout);
262 if idx < storage.len() {
263 trace!(
264 "LRU connection pool: idle timeout was triggered, dropping connections with index {idx:?} and later"
265 );
266 storage.drain(idx..);
267 }
268 }
269
270 let mut get_conn = || loop {
271 let idx = match self.reuse_strategy {
272 ReuseStrategy::FiFo => storage.iter().position(|stored| &stored.id == id)?,
273 ReuseStrategy::RoundRobin => storage.iter().rposition(|stored| &stored.id == id)?,
274 };
275
276 let pooled_conn = storage.remove(idx)?;
277
278 if let Some(watcher) = pooled_conn
280 .extensions()
281 .get_ref::<ConnectionHealthWatcher>()
282 && watcher.health() == ConnectionHealth::Broken
283 {
284 continue;
285 }
286
287 return Some((idx, pooled_conn));
288 };
289
290 if let Some((idx, pooled_conn)) = get_conn() {
291 trace!("LRU connection pool: connection #{idx} found for given id {id:?}");
292
293 #[cfg(feature = "opentelemetry")]
294 if let Some((metrics, metric_attrs)) = &metrics {
295 metrics.total_connections.add(1, metric_attrs);
296 metrics.reused_connections.add(1, metric_attrs);
297 }
298
299 return Ok(ConnectionResult::Connection(LeasedConnection {
300 active_slot,
301 pooled_conn: ManuallyDrop::new(pooled_conn),
302 pooled_conn_taken: false,
303 returner: self.returner.clone(),
304 got_response: AtomicBool::new(false),
305 drop_connection_if_no_response: self.drop_connection_if_no_response,
306 }));
307 }
308
309 let pool_slot = match self.total_slots.clone().try_acquire_owned() {
310 Ok(permit) => PoolSlot(permit),
311 Err(err) => {
312 trace!(
314 error = %err,
315 "LRU connection pool: evicting lru connection (#{id:?}) to create a new one"
316 );
317 #[cfg(feature = "opentelemetry")]
318 if let Some((metrics, metric_attrs)) = &metrics {
319 metrics.evicted_connections.add(1, metric_attrs);
320 }
321 storage
322 .pop_back()
323 .context("get least recently used connection from storage")?
324 .pool_slot
325 }
326 };
327
328 trace!(
329 "LRU connection pool: no connection for given id {id:?} found, returning create permit"
330 );
331 Ok(ConnectionResult::CreatePermit((active_slot, pool_slot)))
332 }
333
334 async fn create(&self, id: ID, conn: C, permit: Self::CreatePermit) -> Self::Connection {
335 trace!("adding new connection (w/ id {id:?}) to pool");
336 let (active_slot, pool_slot) = permit;
337
338 #[cfg(feature = "opentelemetry")]
339 if let Some(metrics) = &self.metrics.as_ref() {
340 let metric_attrs = metrics.attributes(&id);
341 metrics.total_connections.add(1, &metric_attrs);
342 metrics.created_connections.add(1, &metric_attrs);
343 }
344
345 LeasedConnection {
346 active_slot,
347 returner: self.returner.clone(),
348 pooled_conn: ManuallyDrop::new(PooledConnection {
349 id,
350 conn,
351 pool_slot,
352 last_used: Instant::now(),
353 }),
354 pooled_conn_taken: false,
355 got_response: AtomicBool::new(false),
356 drop_connection_if_no_response: self.drop_connection_if_no_response,
357 }
358 }
359}
360impl<C: Debug, ID: Debug> Debug for PooledConnection<C, ID> {
361 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
362 f.debug_struct("PooledConnection")
363 .field("conn", &self.conn)
364 .field("id", &self.id)
365 .field("pool_slot", &self.pool_slot)
366 .finish()
367 }
368}
369
370impl<C, ID> Debug for LeasedConnection<C, ID>
371where
372 C: Debug + ExtensionsRef,
373 ID: Debug,
374{
375 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
376 f.debug_struct("LeasedConnection")
377 .field("pooled_conn", self.pooled_conn.deref())
378 .field("active_slot", &self.active_slot)
379 .finish()
380 }
381}
382
383impl<C: ExtensionsRef, ID> Deref for LeasedConnection<C, ID> {
384 type Target = C;
385
386 fn deref(&self) -> &Self::Target {
387 &self.pooled_conn.conn
388 }
389}
390
391impl<C: ExtensionsRef, ID> DerefMut for LeasedConnection<C, ID> {
392 fn deref_mut(&mut self) -> &mut Self::Target {
393 &mut self.pooled_conn.conn
394 }
395}
396
397impl<C: ExtensionsRef, ID> AsRef<C> for LeasedConnection<C, ID> {
398 fn as_ref(&self) -> &C {
399 self
400 }
401}
402
403impl<C: ExtensionsRef, ID> AsMut<C> for LeasedConnection<C, ID> {
404 fn as_mut(&mut self) -> &mut C {
405 self
406 }
407}
408
409impl<C: ExtensionsRef, ID> Drop for LeasedConnection<C, ID> {
410 fn drop(&mut self) {
411 if !self.pooled_conn_taken {
412 if self.drop_connection_if_no_response && !self.got_response.load(Ordering::Relaxed) {
413 trace!("LRU connection pool: dropping connection that didn't receive a response");
414 unsafe { ManuallyDrop::drop(&mut self.pooled_conn) };
415 return;
416 }
417 if let Some(watcher) = self.extensions().get_ref::<ConnectionHealthWatcher>()
418 && watcher.health() == ConnectionHealth::Broken
419 {
420 trace!("LRU connection pool: dropping pooled connection that was marked as failed");
421
422 unsafe { ManuallyDrop::drop(&mut self.pooled_conn) };
428 } else {
429 trace!("LRU connection pool: returning pooled connection back to pool");
430
431 let pooled_conn = unsafe { ManuallyDrop::take(&mut self.pooled_conn) };
437 self.returner.return_conn(pooled_conn);
438 }
439 }
440 }
441}
442
443impl<C, ID> Socket for LeasedConnection<C, ID>
447where
448 ID: Send + Sync + 'static,
449 C: Socket + ExtensionsRef,
450{
451 fn local_addr(&self) -> std::io::Result<SocketAddress> {
452 self.as_ref().local_addr()
453 }
454
455 fn peer_addr(&self) -> std::io::Result<SocketAddress> {
456 self.as_ref().peer_addr()
457 }
458}
459
460#[warn(clippy::missing_trait_methods)]
461impl<C, ID> AsyncWrite for LeasedConnection<C, ID>
462where
463 C: AsyncWrite + Unpin + ExtensionsRef,
464 ID: Unpin,
465{
466 fn poll_write(
467 mut self: std::pin::Pin<&mut Self>,
468 cx: &mut std::task::Context<'_>,
469 buf: &[u8],
470 ) -> std::task::Poll<Result<usize, std::io::Error>> {
471 Pin::new(self.deref_mut().as_mut()).poll_write(cx, buf)
472 }
473
474 fn poll_flush(
475 mut self: std::pin::Pin<&mut Self>,
476 cx: &mut std::task::Context<'_>,
477 ) -> std::task::Poll<Result<(), std::io::Error>> {
478 Pin::new(self.deref_mut().as_mut()).poll_flush(cx)
479 }
480
481 fn poll_shutdown(
482 mut self: std::pin::Pin<&mut Self>,
483 cx: &mut std::task::Context<'_>,
484 ) -> std::task::Poll<Result<(), std::io::Error>> {
485 Pin::new(self.deref_mut().as_mut()).poll_shutdown(cx)
486 }
487
488 fn is_write_vectored(&self) -> bool {
489 self.deref().is_write_vectored()
490 }
491
492 fn poll_write_vectored(
493 mut self: Pin<&mut Self>,
494 cx: &mut std::task::Context<'_>,
495 bufs: &[std::io::IoSlice<'_>],
496 ) -> std::task::Poll<Result<usize, std::io::Error>> {
497 Pin::new(self.deref_mut().as_mut()).poll_write_vectored(cx, bufs)
498 }
499}
500
501#[warn(clippy::missing_trait_methods)]
502impl<C, ID> AsyncRead for LeasedConnection<C, ID>
503where
504 C: AsyncRead + Unpin + ExtensionsRef,
505 ID: Unpin,
506{
507 fn poll_read(
508 mut self: Pin<&mut Self>,
509 cx: &mut std::task::Context<'_>,
510 buf: &mut tokio::io::ReadBuf<'_>,
511 ) -> std::task::Poll<std::io::Result<()>> {
512 Pin::new(self.deref_mut().as_mut()).poll_read(cx, buf)
513 }
514}
515
516impl<Input, C, ID> Service<Input> for LeasedConnection<C, ID>
517where
518 ID: Send + Sync + Debug + 'static,
519 C: Service<Input> + ExtensionsRef,
520 Input: Send + 'static,
521{
522 type Output = C::Output;
523 type Error = C::Error;
524
525 async fn serve(&self, input: Input) -> Result<Self::Output, Self::Error> {
526 self.got_response.store(false, Ordering::Relaxed);
527 let result = self.as_ref().serve(input).await;
528 if result.is_ok() {
529 self.got_response.store(true, Ordering::Relaxed);
530 }
531 result
532 }
533}
534
535struct StorageDebugHelper<'a, C, ID: Debug> {
540 deque: &'a VecDeque<PooledConnection<C, ID>>,
541}
542
543impl<'a, C, ID: Debug> Debug for StorageDebugHelper<'a, C, ID> {
544 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
545 f.debug_list()
546 .entries(self.deque.iter().map(|item| &item.id))
547 .finish()
548 }
549}
550
551impl<C, ID: Debug> Debug for LruDropPool<C, ID> {
552 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
553 let mut builder = f.debug_struct("LruDropPool");
554
555 match self.storage.try_lock() {
557 Some(guard) => {
558 let storage_debugger = StorageDebugHelper { deque: &*guard };
559 builder.field("storage", &storage_debugger);
560 }
561 None => {
562 builder.field("storage", &"Mutex(locked)");
563 }
564 };
565
566 builder
567 .field("total_slots", &self.total_slots)
568 .field("active_slots", &self.active_slots)
569 .field("idle_timeout", &self.idle_timeout)
570 .field("reuse_strategy", &self.reuse_strategy)
571 .finish()
572 }
573}
574
575impl<C, ID> Extension for LruDropPool<C, ID>
576where
577 C: Send + 'static,
578 ID: Send + Sync + Debug + 'static,
579{
580}
581
582#[cfg(test)]
583mod tests {
584 use super::super::{PooledConnector, ReqToConnID};
585 use super::*;
586 use crate::client::{ConnectorService, EstablishedClientConnection};
587 use rama_core::ServiceInput;
588 use rama_core::extensions::ExtensionsRef;
589 use rama_core::{Service, extensions::Extensions};
590 use std::sync::atomic::AtomicBool;
591 use std::{
592 convert::Infallible,
593 sync::atomic::{AtomicI16, Ordering},
594 };
595 use tokio_test::assert_ok;
596
597 struct TestService {
598 pub created_connection: AtomicI16,
599 }
600
601 impl Default for TestService {
602 fn default() -> Self {
603 Self {
604 created_connection: AtomicI16::new(0),
605 }
606 }
607 }
608
609 #[derive(Debug)]
610 struct Conn {
611 items: Vec<u32>,
612 extensions: Extensions,
613 }
614
615 impl Conn {
616 fn new() -> Self {
617 Self {
618 items: vec![],
619 extensions: Extensions::new(),
620 }
621 }
622 }
623
624 impl ExtensionsRef for Conn {
625 fn extensions(&self) -> &Extensions {
626 &self.extensions
627 }
628 }
629
630 impl Deref for Conn {
631 type Target = Vec<u32>;
632
633 fn deref(&self) -> &Self::Target {
634 &self.items
635 }
636 }
637
638 impl DerefMut for Conn {
639 fn deref_mut(&mut self) -> &mut Self::Target {
640 &mut self.items
641 }
642 }
643
644 impl<Input> Service<Input> for TestService
645 where
646 Input: Send + 'static,
647 {
648 type Output = EstablishedClientConnection<Conn, Input>;
649 type Error = Infallible;
650
651 async fn serve(&self, input: Input) -> Result<Self::Output, Self::Error> {
652 self.created_connection.fetch_add(1, Ordering::Relaxed);
653 Ok(EstablishedClientConnection {
654 input,
655 conn: Conn::new(),
656 })
657 }
658 }
659
660 #[derive(Clone)]
661 struct StringInputLengthID;
665
666 impl ReqToConnID<ServiceInput<String>> for StringInputLengthID {
667 type ID = usize;
668
669 fn id(&self, input: &ServiceInput<String>) -> Result<Self::ID, BoxError> {
670 Ok(input.input.chars().count())
671 }
672 }
673
674 impl ConnID for usize {}
675 impl ConnID for () {}
676
677 #[tokio::test]
678 async fn test_should_reuse_connections() {
679 let pool = LruDropPool::try_new(5, 10)
680 .unwrap()
681 .with_drop_connection_if_no_response(false);
682 let svc = PooledConnector::new(
685 TestService::default(),
686 pool,
687 |__req: &ServiceInput<String>| Ok(()),
688 );
689
690 let iterations = 10;
691 for _i in 0..iterations {
692 let _conn = svc.connect(ServiceInput::new(String::new())).await.unwrap();
693 }
694
695 let created_connection = svc.inner.created_connection.load(Ordering::Relaxed);
696 assert_eq!(created_connection, 1);
697 }
698
699 #[tokio::test]
700 async fn test_conn_id_to_separate() {
701 let pool = LruDropPool::try_new(5, 10)
702 .unwrap()
703 .with_drop_connection_if_no_response(false);
704 let svc = PooledConnector::new(TestService::default(), pool, StringInputLengthID {});
705
706 {
707 let mut conn = svc
708 .connect(ServiceInput::new(String::from("a")))
709 .await
710 .unwrap()
711 .conn;
712
713 conn.push(1);
714 assert_eq!(conn.as_ref().deref(), &vec![1]);
715 assert_eq!(svc.inner.created_connection.load(Ordering::Relaxed), 1);
716 }
717
718 {
720 let mut conn = svc
721 .connect(ServiceInput::new(String::from("B")))
722 .await
723 .unwrap()
724 .conn;
725
726 conn.push(2);
727 assert_eq!(conn.as_ref().deref(), &vec![1, 2]);
728 assert_eq!(svc.inner.created_connection.load(Ordering::Relaxed), 1);
729 }
730
731 {
733 let mut conn = svc
734 .connect(ServiceInput::new(String::from("aa")))
735 .await
736 .unwrap()
737 .conn;
738
739 conn.push(3);
740 assert_eq!(conn.as_ref().deref(), &vec![3]);
741 assert_eq!(svc.inner.created_connection.load(Ordering::Relaxed), 2);
742 }
743
744 {
746 let mut conn = svc
747 .connect(ServiceInput::new(String::from("bb")))
748 .await
749 .unwrap()
750 .conn;
751
752 conn.push(4);
753 assert_eq!(conn.as_ref().deref(), &vec![3, 4]);
754 assert_eq!(svc.inner.created_connection.load(Ordering::Relaxed), 2);
755 }
756 }
757
758 #[tokio::test]
759 async fn test_pool_max_size() {
760 let pool = LruDropPool::try_new(1, 1)
761 .unwrap()
762 .with_drop_connection_if_no_response(false);
763 let svc = PooledConnector::new(TestService::default(), pool, StringInputLengthID {})
764 .with_wait_for_pool_timeout(Duration::from_millis(50));
765
766 let conn1 = svc
767 .connect(ServiceInput::new(String::from("a")))
768 .await
769 .unwrap();
770
771 let conn2 = svc.connect(ServiceInput::new(String::from("a"))).await;
772 let _error = conn2.unwrap_err();
773
774 drop(conn1);
775 let _conn3 = svc
776 .connect(ServiceInput::new(String::from("aaa")))
777 .await
778 .unwrap();
779 }
780
781 #[derive(Default)]
782 struct TestConnector {
783 pub created_connection: AtomicI16,
784 }
785
786 impl<Input> Service<Input> for TestConnector
787 where
788 Input: Send + 'static,
789 {
790 type Output = EstablishedClientConnection<InnerService, Input>;
791 type Error = Infallible;
792
793 async fn serve(&self, input: Input) -> Result<Self::Output, Self::Error> {
794 let conn = InnerService::default();
795
796 conn.extensions().insert(ConnectionHealthWatcher::default());
797
798 self.created_connection.fetch_add(1, Ordering::Relaxed);
799 Ok(EstablishedClientConnection { input, conn })
800 }
801 }
802
803 #[derive(Default, Debug)]
804 struct InnerService {
805 should_error: Arc<AtomicBool>,
806 extensions: Extensions,
807 }
808
809 impl ExtensionsRef for InnerService {
810 fn extensions(&self) -> &Extensions {
811 &self.extensions
812 }
813 }
814
815 impl Service<bool> for InnerService {
816 type Output = ();
817 type Error = BoxError;
818
819 async fn serve(&self, should_error: bool) -> Result<Self::Output, Self::Error> {
820 if should_error {
822 self.extensions
823 .get_ref::<ConnectionHealthWatcher>()
824 .unwrap()
825 .mark_broken();
826 self.should_error.store(true, Ordering::Relaxed);
827 }
828
829 if self.should_error.load(Ordering::Relaxed) {
830 Err(BoxError::from_static_str("service is in broken state"))
831 } else {
832 Ok(())
833 }
834 }
835 }
836
837 impl Service<Duration> for InnerService {
838 type Output = ();
839 type Error = BoxError;
840
841 async fn serve(&self, delay: Duration) -> Result<Self::Output, Self::Error> {
842 tokio::time::sleep(delay).await;
843 Ok(())
844 }
845 }
846
847 #[tokio::test]
848 async fn test_cancellated_fut_should_drop_connection_by_default() {
849 let pool = LruDropPool::try_new(1, 1).unwrap();
850 let svc = PooledConnector::new(TestConnector::default(), pool, StringInputLengthID {});
851
852 let conn = svc
853 .connect(ServiceInput::new(String::from("")))
854 .await
855 .unwrap();
856 assert_ok!(conn.conn.serve(false).await);
857 drop(conn);
858 assert_eq!(svc.inner.created_connection.load(Ordering::Relaxed), 1);
859
860 let conn = svc
862 .connect(ServiceInput::new(String::from("")))
863 .await
864 .unwrap();
865 assert_eq!(svc.inner.created_connection.load(Ordering::Relaxed), 1);
866
867 let timeout_result = tokio::time::timeout(
868 Duration::from_millis(10),
869 conn.conn.serve(Duration::from_secs(60)),
870 )
871 .await;
872
873 assert!(timeout_result.is_err(), "should have timed out");
874 drop(conn);
875
876 let conn = svc
878 .connect(ServiceInput::new(String::from("")))
879 .await
880 .unwrap();
881 assert_eq!(svc.inner.created_connection.load(Ordering::Relaxed), 2);
882 assert_ok!(conn.conn.serve(false).await);
883 }
884
885 #[tokio::test]
886 async fn test_cancellated_fut_should_not_drop_connection_if_this_is_disabled() {
887 let pool = LruDropPool::try_new(1, 1)
888 .unwrap()
889 .with_drop_connection_if_no_response(false);
890 let svc = PooledConnector::new(TestConnector::default(), pool, StringInputLengthID {});
891
892 let conn = svc
893 .connect(ServiceInput::new(String::from("")))
894 .await
895 .unwrap();
896 assert_eq!(svc.inner.created_connection.load(Ordering::Relaxed), 1);
897
898 let timeout_result = tokio::time::timeout(
899 Duration::from_millis(10),
900 conn.conn.serve(Duration::from_secs(60)),
901 )
902 .await;
903 assert!(timeout_result.is_err(), "should have timed out");
904 drop(conn);
905
906 let conn = svc
908 .connect(ServiceInput::new(String::from("")))
909 .await
910 .unwrap();
911 assert_eq!(svc.inner.created_connection.load(Ordering::Relaxed), 1);
912 assert_ok!(conn.conn.serve(false).await);
913 }
914
915 #[tokio::test]
916 async fn test_dont_return_broken_connections_to_pool() {
917 let pool = LruDropPool::try_new(1, 1).unwrap();
918 let svc = PooledConnector::new(TestConnector::default(), pool, StringInputLengthID {});
919
920 let conn = svc
921 .connect(ServiceInput::new(String::from("")))
922 .await
923 .unwrap();
924
925 let result = conn.conn.serve(false).await;
926 assert_ok!(result);
927 let result = conn.conn.serve(true).await;
928 let _error = result.unwrap_err();
929
930 drop(conn);
932
933 let conn = svc
934 .connect(ServiceInput::new(String::from("")))
935 .await
936 .unwrap();
937
938 let result = conn.conn.serve(false).await;
939 assert_ok!(result);
940
941 drop(conn);
943
944 let conn = svc
945 .connect(ServiceInput::new(String::from("")))
946 .await
947 .unwrap();
948
949 let result = conn.conn.serve(false).await;
950 assert_ok!(result);
951
952 assert_eq!(svc.inner.created_connection.load(Ordering::Relaxed), 2);
953 }
954
955 #[tokio::test]
956 async fn test_pool_drops_broken_connections_in_get_conn() {
957 let pool = LruDropPool::try_new(1, 1).unwrap();
958 let svc = PooledConnector::new(TestConnector::default(), pool, StringInputLengthID {});
959
960 let conn = svc
961 .connect(ServiceInput::new(String::from("")))
962 .await
963 .unwrap();
964
965 let result = conn.conn.serve(false).await;
966 assert_ok!(result);
967
968 let conn_extensions = conn.conn.extensions().clone();
970 drop(conn);
971
972 conn_extensions
976 .get_ref::<ConnectionHealthWatcher>()
977 .unwrap()
978 .mark_broken();
979
980 let conn = svc
982 .connect(ServiceInput::new(String::from("")))
983 .await
984 .unwrap();
985
986 let result = conn.conn.serve(false).await;
987 assert_ok!(result);
988
989 drop(conn);
991
992 let conn = svc
994 .connect(ServiceInput::new(String::from("")))
995 .await
996 .unwrap();
997
998 let result = conn.conn.serve(false).await;
999 assert_ok!(result);
1000
1001 assert_eq!(svc.inner.created_connection.load(Ordering::Relaxed), 2);
1002 }
1003
1004 #[tokio::test]
1005 async fn drop_idle_connections() {
1006 let pool = LruDropPool::try_new(5, 10)
1007 .unwrap()
1008 .with_idle_timeout(Duration::from_micros(1))
1009 .with_drop_connection_if_no_response(false);
1010
1011 let svc = PooledConnector::new(TestService::default(), pool, StringInputLengthID {});
1012
1013 let conn = svc
1014 .connect(ServiceInput::new(String::from("")))
1015 .await
1016 .unwrap()
1017 .conn;
1018
1019 assert_eq!(svc.inner.created_connection.load(Ordering::Relaxed), 1);
1020 drop(conn);
1021 tokio::time::sleep(Duration::from_millis(100)).await;
1025
1026 let conn = svc
1027 .connect(ServiceInput::new(String::from("")))
1028 .await
1029 .unwrap()
1030 .conn;
1031
1032 assert_eq!(svc.inner.created_connection.load(Ordering::Relaxed), 2);
1033 drop(conn);
1034 }
1035
1036 #[tokio::test]
1037 async fn fifo_reuse() {
1038 test_reuse(ReuseStrategy::FiFo, 1).await;
1039 }
1040
1041 #[tokio::test]
1042 async fn round_robin_reuse() {
1043 test_reuse(ReuseStrategy::RoundRobin, 0).await;
1044 }
1045
1046 async fn test_reuse(strategy: ReuseStrategy, expected: u32) {
1047 let pool = LruDropPool::try_new(5, 10)
1048 .unwrap()
1049 .with_reuse_strategy(strategy)
1050 .with_drop_connection_if_no_response(false);
1051
1052 let svc = PooledConnector::new(TestService::default(), pool, |_: &ServiceInput<()>| Ok(()));
1053
1054 let mut conns = Vec::new();
1056 for i in 0..2 {
1057 let mut conn = svc.connect(ServiceInput::new(())).await.unwrap().conn;
1058 conn.pooled_conn.conn.push(i);
1059 conns.push(conn);
1060 }
1061
1062 drop(conns);
1063
1064 let conn = svc.connect(ServiceInput::new(())).await.unwrap().conn;
1069
1070 assert_eq!(conn.pooled_conn.conn[0], expected);
1071 }
1072}