1use std::sync::Arc;
4use tokio::sync::{Mutex, Semaphore};
5
6use crate::client::{Client, Connection};
7use crate::error::{Error, Result};
8
9pub struct ConnectionPool {
11 client: Client,
12 connections: Arc<Mutex<Vec<Connection>>>,
13 semaphore: Arc<Semaphore>,
14 max_size: usize,
15}
16
17impl ConnectionPool {
18 pub fn new(host: impl Into<String>, port: u16, max_size: usize) -> Self {
25 assert!(
26 max_size > 0,
27 "ConnectionPool max_size must be at least 1 (was 0). \
28 A pool with 0 connections would deadlock on acquire()."
29 );
30 Self {
31 client: Client::new(host, port),
32 connections: Arc::new(Mutex::new(Vec::new())),
33 semaphore: Arc::new(Semaphore::new(max_size)),
34 max_size,
35 }
36 }
37
38 pub fn skip_verify(mut self, skip: bool) -> Self {
40 self.client = self.client.skip_verify(skip);
41 self
42 }
43
44 pub fn page_size(mut self, size: usize) -> Self {
46 self.client = self.client.page_size(size);
47 self
48 }
49
50 pub fn username(mut self, username: impl Into<String>) -> Self {
52 self.client = self.client.username(username);
53 self
54 }
55
56 pub fn password(mut self, password: impl Into<String>) -> Self {
58 self.client = self.client.password(password);
59 self
60 }
61
62 pub async fn acquire(&self) -> Result<PooledConnection> {
68 let permit = Arc::clone(&self.semaphore)
69 .acquire_owned()
70 .await
71 .map_err(|_| Error::pool("Connection pool has been closed"))?;
72
73 let connection = loop {
75 let conn = {
76 let mut connections = self.connections.lock().await;
77 connections.pop()
78 };
79
80 match conn {
81 Some(c) if c.is_healthy() => {
82 break c;
84 }
85 Some(_) => {
86 continue;
89 }
90 None => {
91 let client = self.client.clone();
93 break client.connect().await?;
94 }
95 }
96 };
97
98 Ok(PooledConnection {
99 connection: Some(connection),
100 pool: self.connections.clone(),
101 _permit: permit,
102 })
103 }
104
105 pub async fn size(&self) -> usize {
107 self.connections.lock().await.len()
108 }
109
110 pub fn max_size(&self) -> usize {
112 self.max_size
113 }
114}
115
116pub struct PooledConnection {
118 connection: Option<Connection>,
119 pool: Arc<Mutex<Vec<Connection>>>,
120 _permit: tokio::sync::OwnedSemaphorePermit,
121}
122
123impl PooledConnection {
124 pub fn inner(&self) -> &Connection {
132 self.connection
133 .as_ref()
134 .expect("PooledConnection invariant violated: connection was None")
135 }
136}
137
138impl Drop for PooledConnection {
139 fn drop(&mut self) {
140 if let Some(mut conn) = self.connection.take() {
141 if conn.is_healthy() {
144 let pool = self.pool.clone();
145 tokio::spawn(async move {
146 if conn.in_transaction() {
148 let _ = conn.rollback().await;
149 }
150 let mut connections = pool.lock().await;
151 connections.push(conn);
152 });
153 }
154 }
156 }
157}
158
159impl std::ops::Deref for PooledConnection {
160 type Target = Connection;
161
162 fn deref(&self) -> &Self::Target {
163 self.connection
164 .as_ref()
165 .expect("PooledConnection invariant violated: connection was None")
166 }
167}
168
169impl std::ops::DerefMut for PooledConnection {
170 fn deref_mut(&mut self) -> &mut Self::Target {
171 self.connection
172 .as_mut()
173 .expect("PooledConnection invariant violated: connection was None")
174 }
175}
176
177#[cfg(test)]
178mod tests {
179 use super::*;
180
181 #[test]
182 fn test_connection_pool_new() {
183 let pool = ConnectionPool::new("localhost", 3141, 10);
184 assert_eq!(pool.max_size(), 10);
185 }
186
187 #[test]
188 fn test_connection_pool_new_different_host() {
189 let pool = ConnectionPool::new("192.168.1.100", 8443, 5);
190 assert_eq!(pool.max_size(), 5);
191 }
192
193 #[test]
194 fn test_connection_pool_new_string_host() {
195 let host = String::from("geode.example.com");
196 let pool = ConnectionPool::new(host, 3141, 20);
197 assert_eq!(pool.max_size(), 20);
198 }
199
200 #[test]
201 fn test_connection_pool_skip_verify() {
202 let pool = ConnectionPool::new("localhost", 3141, 10).skip_verify(true);
203 assert_eq!(pool.max_size(), 10);
205 }
206
207 #[test]
208 fn test_connection_pool_skip_verify_false() {
209 let pool = ConnectionPool::new("localhost", 3141, 10).skip_verify(false);
210 assert_eq!(pool.max_size(), 10);
211 }
212
213 #[test]
214 fn test_connection_pool_page_size() {
215 let pool = ConnectionPool::new("localhost", 3141, 10).page_size(500);
216 assert_eq!(pool.max_size(), 10);
217 }
218
219 #[test]
220 fn test_connection_pool_chained_config() {
221 let pool = ConnectionPool::new("localhost", 3141, 10)
222 .skip_verify(true)
223 .page_size(1000);
224 assert_eq!(pool.max_size(), 10);
225 }
226
227 #[tokio::test]
228 async fn test_connection_pool_initial_size() {
229 let pool = ConnectionPool::new("localhost", 3141, 10);
230 assert_eq!(pool.size().await, 0);
232 }
233
234 #[test]
235 #[should_panic(expected = "ConnectionPool max_size must be at least 1")]
236 fn test_connection_pool_max_size_zero_panics() {
237 let _pool = ConnectionPool::new("localhost", 3141, 0);
240 }
241
242 #[test]
243 fn test_connection_pool_max_size_one() {
244 let pool = ConnectionPool::new("localhost", 3141, 1);
245 assert_eq!(pool.max_size(), 1);
246 }
247
248 #[test]
249 fn test_connection_pool_max_size_large() {
250 let pool = ConnectionPool::new("localhost", 3141, 1000);
251 assert_eq!(pool.max_size(), 1000);
252 }
253
254 #[test]
263 fn test_semaphore_permits_match_max_size() {
264 let pool = ConnectionPool::new("localhost", 3141, 5);
265 assert_eq!(pool.semaphore.available_permits(), 5);
267 }
268
269 #[test]
270 fn test_connections_vec_initially_empty() {
271 let pool = ConnectionPool::new("localhost", 3141, 10);
272 assert_eq!(pool.max_size(), 10);
275 }
276}