connpool/pools/
registry.rs1use std::collections::HashMap;
5use std::fmt::Display;
6use std::hash::Hash;
7use std::sync::{Arc, Mutex};
8
9use crate::core::clock::Clock;
10use crate::core::errors::PoolError;
11use crate::core::timeouts::Timeout;
12use crate::factories::sync_factory::ConnectionFactory;
13use crate::observability::events::PoolListener;
14use crate::policy::config::PoolConfig;
15use crate::pools::sync_pool::{ConnectionPool, PoolOptions};
16
17type FactoryFor<K, T> = Box<dyn Fn(&K) -> Box<dyn ConnectionFactory<T>> + Send + Sync>;
18
19struct RegState<K, T> {
20 pools: HashMap<K, ConnectionPool<T>>,
21 closed: bool,
22}
23
24pub struct PoolRegistry<K, T> {
28 factory_for: FactoryFor<K, T>,
29 config: PoolConfig,
30 clock: Option<Arc<dyn Clock>>,
31 listeners: Vec<Arc<dyn PoolListener<T>>>,
32 inner: Mutex<RegState<K, T>>,
33}
34
35impl<K, T> PoolRegistry<K, T>
36where
37 K: Clone + Eq + Hash + Display,
38 T: Clone + Eq + Hash + Send + Sync + 'static,
39{
40 pub fn new<F>(factory_for: F, config: PoolConfig) -> Self
43 where
44 F: Fn(&K) -> Box<dyn ConnectionFactory<T>> + Send + Sync + 'static,
45 {
46 Self {
47 factory_for: Box::new(factory_for),
48 config,
49 clock: None,
50 listeners: Vec::new(),
51 inner: Mutex::new(RegState {
52 pools: HashMap::new(),
53 closed: false,
54 }),
55 }
56 }
57
58 pub fn with_clock(mut self, clock: Arc<dyn Clock>) -> Self {
60 self.clock = Some(clock);
61 self
62 }
63
64 pub fn with_listeners(mut self, listeners: Vec<Arc<dyn PoolListener<T>>>) -> Self {
66 self.listeners = listeners;
67 self
68 }
69
70 pub fn pool(&self, key: &K) -> Result<ConnectionPool<T>, PoolError> {
73 let mut g = self.inner.lock().unwrap();
74 if g.closed {
75 return Err(PoolError::Closed("registry is closed".to_string()));
76 }
77 if let Some(pool) = g.pools.get(key) {
78 return Ok(pool.clone());
79 }
80 let options = PoolOptions {
81 name: Some(key.to_string()),
82 clock: self.clock.clone(),
83 rng: None,
84 listeners: self.listeners.clone(),
85 };
86 let pool =
87 ConnectionPool::new((self.factory_for)(key), self.config.clone(), Some(options))?;
88 g.pools.insert(key.clone(), pool.clone());
89 Ok(pool)
90 }
91
92 pub fn with_connection<R>(
94 &self,
95 key: &K,
96 timeout: Timeout,
97 f: impl FnOnce(&T) -> R,
98 ) -> Result<R, PoolError> {
99 self.pool(key)?.with_connection(timeout, f)
100 }
101
102 pub fn close(&self) {
104 let pools = {
105 let mut g = self.inner.lock().unwrap();
106 g.closed = true;
107 std::mem::take(&mut g.pools)
108 };
109 for (_key, pool) in pools {
110 pool.close(None);
111 }
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118 use crate::test_support::*;
119
120 fn registry(h: &Harness) -> PoolRegistry<String, Conn> {
121 let fac = h.fac.clone();
122 let rec: Arc<dyn PoolListener<Conn>> = Recorder::new();
123 PoolRegistry::new(
124 move |_key: &String| {
125 Box::new(TestFactory(fac.clone())) as Box<dyn ConnectionFactory<Conn>>
126 },
127 PoolConfig {
128 maintenance_interval: None,
129 breaker: None,
130 ..Default::default()
131 },
132 )
133 .with_clock(h.clock.clone())
134 .with_listeners(vec![rec])
135 }
136
137 #[test]
138 fn pool_is_created_once_per_key() {
139 let h = Harness::new();
140 let reg = registry(&h);
141 let p1 = reg.pool(&"a".to_string()).unwrap();
142 let p2 = reg.pool(&"a".to_string()).unwrap();
143 assert_eq!(p1.name(), "a");
144 assert_eq!(p2.name(), "a");
145 let _b = reg.pool(&"b".to_string()).unwrap();
146 let id = reg
148 .with_connection(&"a".to_string(), Timeout::Unset, |c| c.id)
149 .unwrap();
150 assert!(id >= 1);
151 }
152
153 #[test]
154 fn close_rejects_further_use() {
155 let h = Harness::new();
156 let reg = registry(&h);
157 let p = reg.pool(&"a".to_string()).unwrap();
158 reg.close();
159 assert!(p.is_closed());
160 assert!(matches!(
161 reg.pool(&"a".to_string()),
162 Err(PoolError::Closed(_))
163 ));
164 }
165}