1use std::{
4 future::Future,
5 ops::Deref,
6 sync::{
7 Arc, Mutex, Weak,
8 atomic::{AtomicBool, Ordering},
9 },
10 time::Instant,
11};
12
13use tokio::sync::{Notify, OwnedSemaphorePermit, Semaphore};
14
15use crate::{
16 config::{Config, PoolConfig},
17 connection::{Connection, RowStream},
18 encode::ToSql,
19 error::Error,
20 transaction::Transaction,
21};
22
23pub(crate) struct IdleConn {
24 pub(crate) conn: Connection,
25 pub(crate) created: Instant,
26 pub(crate) idle_since: Instant,
27 pub(crate) permit: OwnedSemaphorePermit,
28}
29
30pub(crate) struct PoolInner {
31 config: Config,
32 settings: PoolConfig,
33 idle: Mutex<Vec<IdleConn>>,
34 semaphore: Arc<Semaphore>,
35 closed: AtomicBool,
36 notify: Notify,
37}
38
39impl PoolInner {
40 fn lock_idle(&self) -> std::sync::MutexGuard<'_, Vec<IdleConn>> {
41 self.idle.lock().unwrap_or_else(|poisoned| poisoned.into_inner())
42 }
43
44 pub(crate) fn pop_idle(&self) -> Option<IdleConn> {
45 let mut idle = self.lock_idle();
46 loop {
47 let conn = idle.pop()?;
48 if conn.conn.is_closed() {
49 continue;
52 }
53 return Some(conn);
54 }
55 }
56
57 pub(crate) fn push_idle(&self, conn: Connection, permit: OwnedSemaphorePermit, created: Instant) {
58 {
59 let mut idle = self.lock_idle();
60 if self.closed.load(Ordering::Acquire) {
61 return;
65 }
66 idle.push(IdleConn {
67 conn,
68 created,
69 idle_since: Instant::now(),
70 permit,
71 });
72 }
73 self.notify.notify_one();
74 }
75
76 pub(crate) fn is_closed(&self) -> bool {
77 self.closed.load(Ordering::Acquire)
78 }
79}
80
81pub(crate) struct Checkout {
89 conn: Option<Connection>,
90 pool: Option<Arc<PoolInner>>,
91 permit: Option<OwnedSemaphorePermit>,
92 created: Instant,
93}
94
95impl Checkout {
96 pub(crate) fn pooled(
98 conn: Connection,
99 pool: Arc<PoolInner>,
100 permit: OwnedSemaphorePermit,
101 created: Instant,
102 ) -> Self {
103 Checkout {
104 conn: Some(conn),
105 pool: Some(pool),
106 permit: Some(permit),
107 created,
108 }
109 }
110
111 pub(crate) fn detached(conn: Connection) -> Self {
113 Checkout {
114 conn: Some(conn),
115 pool: None,
116 permit: None,
117 created: Instant::now(),
118 }
119 }
120
121 pub(crate) fn connection(&self) -> &Connection {
122 self.conn.as_ref().expect("checkout already returned")
123 }
124}
125
126impl Drop for Checkout {
127 fn drop(&mut self) {
128 let Some(conn) = self.conn.take() else { return };
129 match self.pool.take() {
130 Some(pool) => {
131 let Some(permit) = self.permit.take() else { return };
132 if pool.is_closed() || conn.is_closed() {
133 drop(permit);
136 drop(conn);
137 } else {
138 pool.push_idle(conn, permit, self.created);
139 }
140 }
141 None => drop(conn),
142 }
143 }
144}
145
146#[derive(Clone)]
148pub struct Pool {
149 inner: Arc<PoolInner>,
150}
151
152impl Pool {
153 pub async fn connect(url: &str, config: Option<PoolConfig>) -> Result<Self, Error> {
198 let mut parsed = Config::parse(url)?;
199 if let Some(overrides) = config {
200 parsed.max_message_len = overrides.max_message_len;
202 parsed.pool = overrides;
203 }
204 Pool::from_config(parsed).await
205 }
206
207 async fn from_config(config: Config) -> Result<Self, Error> {
208 let settings = config.pool.clone();
209 let pool = Pool {
210 inner: Arc::new(PoolInner {
211 config,
212 settings: settings.clone(),
213 idle: Mutex::new(Vec::new()),
214 semaphore: Arc::new(Semaphore::new(settings.pool_size as usize)),
215 closed: AtomicBool::new(false),
216 notify: Notify::new(),
217 }),
218 };
219
220 for _ in 0..settings.min_connections {
221 let permit = match pool.inner.semaphore.clone().acquire_owned().await {
222 Ok(permit) => permit,
223 Err(_) => break,
224 };
225 match Connection::connect_with_config(pool.inner.config.clone()).await {
226 Ok(conn) => pool.inner.push_idle(conn, permit, Instant::now()),
227 Err(_) => break,
228 }
229 }
230
231 tokio::spawn(Self::reap_loop(Arc::downgrade(&pool.inner)));
235
236 Ok(pool)
237 }
238
239 pub async fn get(&self) -> Result<PooledConnection, Error> {
247 if self.inner.is_closed() {
248 return Err(Error::PoolClosed);
249 }
250
251 loop {
252 if let Some(idle) = self.inner.pop_idle() {
253 return Ok(self.wrap_idle(idle));
254 }
255
256 let notified = self.inner.notify.notified();
257
258 if self.inner.is_closed() {
259 return Err(Error::PoolClosed);
260 }
261 if let Some(idle) = self.inner.pop_idle() {
262 return Ok(self.wrap_idle(idle));
263 }
264
265 tokio::select! {
266 _ = notified => continue,
267 acquired = tokio::time::timeout(
268 self.inner.settings.acquire_timeout,
269 self.inner.semaphore.clone().acquire_owned(),
270 ) => {
271 let permit = acquired
272 .map_err(|_| Error::PoolTimedOut)?
273 .map_err(|_| Error::PoolClosed)?;
274
275 if let Some(idle) = self.inner.pop_idle() {
277 drop(permit);
278 return Ok(self.wrap_idle(idle));
279 }
280
281 let conn = Connection::connect_with_config(self.inner.config.clone()).await?;
282 return Ok(PooledConnection {
283 checkout: Some(Arc::new(Checkout::pooled(
284 conn,
285 self.inner.clone(),
286 permit,
287 Instant::now(),
288 ))),
289 });
290 }
291 }
292 }
293 }
294
295 fn wrap_idle(&self, idle: IdleConn) -> PooledConnection {
296 PooledConnection {
297 checkout: Some(Arc::new(Checkout::pooled(
298 idle.conn,
299 self.inner.clone(),
300 idle.permit,
301 idle.created,
302 ))),
303 }
304 }
305
306 pub async fn begin(&self) -> Result<Transaction, Error> {
308 let pooled = self.get().await?;
309 pooled.begin().await
310 }
311
312 pub fn is_closed(&self) -> bool {
314 self.inner.is_closed()
315 }
316
317 pub fn close(&self) {
322 self.inner.closed.store(true, Ordering::Release);
323 self.inner.semaphore.close();
324 self.inner.lock_idle().clear();
325 self.inner.notify.notify_waiters();
326 }
327
328 async fn reap_loop(inner: Weak<PoolInner>) {
329 loop {
330 tokio::time::sleep(std::time::Duration::from_secs(30)).await;
331 let Some(inner) = inner.upgrade() else { return };
333 if inner.is_closed() {
334 return;
335 }
336 let now = Instant::now();
337 let idle_timeout = inner.settings.idle_timeout;
338 let max_lifetime = inner.settings.max_lifetime;
339 let min = inner.settings.min_connections as usize;
340
341 let mut idle = inner.lock_idle();
342 let mut keep: Vec<IdleConn> = Vec::with_capacity(idle.len());
343 let mut expired: Vec<IdleConn> = Vec::new();
344 for conn in idle.drain(..) {
345 let is_expired = now.duration_since(conn.idle_since) > idle_timeout
346 || now.duration_since(conn.created) > max_lifetime;
347 if is_expired {
348 expired.push(conn);
349 } else {
350 keep.push(conn);
351 }
352 }
353 while keep.len() < min {
355 let Some(conn) = expired.pop() else { break };
356 keep.push(conn);
357 }
358 drop(expired);
359 *idle = keep;
360 }
361 }
362}
363
364impl crate::query::sealed::Sealed for Pool {}
365
366impl crate::query::Capable for Pool {
367 fn execute<'a>(
368 &'a self,
369 sql: &'a str,
370 params: &'a [&'a dyn ToSql],
371 ) -> impl Future<Output = Result<u64, Error>> + Send + 'a {
372 async move {
373 let conn = self.get().await?;
374 conn.execute(sql, params).await
375 }
376 }
377
378 fn fetch_rows<'a>(
379 &'a self,
380 sql: &'a str,
381 params: &'a [&'a dyn ToSql],
382 max_rows: i32,
383 ) -> impl Future<Output = Result<RowStream, Error>> + Send + 'a {
384 async move {
385 let conn = self.get().await?;
389 conn.fetch_rows(sql, params, max_rows).await
390 }
391 }
392}
393
394impl crate::query::ScriptExecutor for Pool {
395 fn execute_script<'a>(&'a self, sql: &'a str) -> impl Future<Output = Result<u64, Error>> + Send + 'a {
396 async move {
397 let conn = self.get().await?;
398 conn.execute_script(sql).await
399 }
400 }
401}
402
403pub struct PooledConnection {
410 checkout: Option<Arc<Checkout>>,
411}
412
413impl PooledConnection {
414 pub async fn begin(mut self) -> Result<Transaction, Error> {
416 let checkout = self.checkout.take().expect("connection already taken");
417 Transaction::begin_checkout(checkout).await
418 }
419
420 fn checkout(&self) -> &Checkout {
421 self.checkout.as_deref().expect("connection already taken")
422 }
423}
424
425impl Deref for PooledConnection {
426 type Target = Connection;
427
428 fn deref(&self) -> &Connection {
429 self.checkout().connection()
430 }
431}
432
433impl Drop for PooledConnection {
434 fn drop(&mut self) {
435 let _ = self.checkout.take();
438 }
439}
440
441impl crate::query::sealed::Sealed for PooledConnection {}
442
443impl crate::query::Capable for PooledConnection {
444 fn execute<'a>(
445 &'a self,
446 sql: &'a str,
447 params: &'a [&'a dyn ToSql],
448 ) -> impl Future<Output = Result<u64, Error>> + Send + 'a {
449 async move { self.checkout().connection().execute(sql, params).await }
450 }
451
452 fn fetch_rows<'a>(
453 &'a self,
454 sql: &'a str,
455 params: &'a [&'a dyn ToSql],
456 max_rows: i32,
457 ) -> impl Future<Output = Result<RowStream, Error>> + Send + 'a {
458 async move {
459 let checkout = self.checkout.as_ref().expect("connection already taken").clone();
462 let stream = checkout.connection().fetch_rows(sql, params, max_rows).await?;
463 Ok(stream.with_keepalive(checkout))
464 }
465 }
466}
467
468impl crate::query::ScriptExecutor for PooledConnection {
469 fn execute_script<'a>(&'a self, sql: &'a str) -> impl Future<Output = Result<u64, Error>> + Send + 'a {
470 async move { self.checkout().connection().execute_script(sql).await }
471 }
472}