Skip to main content

tank_core/
pool.rs

1use crate::{
2    AsEntity, AsQuery, Connection, Driver, Error, Executor, Query, QueryResult, Result, Row,
3    RowsAffected,
4};
5use anyhow::anyhow;
6use deadpool::managed::{Manager, Metrics, Object, Pool, RecycleResult, Timeouts};
7use futures::{FutureExt, Stream, future::BoxFuture};
8use std::{
9    borrow::Cow,
10    fmt::Debug,
11    future,
12    ops::{Deref, DerefMut},
13    sync::Arc,
14    time::Duration,
15};
16
17/// The [`Manager`] that backs every Tank connection pool.
18///
19/// A manager holds the [`Driver`] and the database URL; it knows how to open
20/// new connections on demand and how to validate recycled ones.  You do not
21/// need to construct or interact with this type directly, it is created
22/// internally by [`Driver::connect_pool`].
23#[derive(Debug)]
24pub struct DBConnectionManager<D: Driver> {
25    driver: D,
26    url: Cow<'static, str>,
27}
28
29impl<D: Driver> DBConnectionManager<D> {
30    pub fn new(driver: D, url: Cow<'static, str>) -> Self {
31        Self { driver, url }
32    }
33}
34
35impl<D: Driver> Manager for DBConnectionManager<D> {
36    type Type = D::Connection;
37    type Error = Error;
38    async fn create(&self) -> Result<Self::Type> {
39        Ok(D::Connection::connect(&self.driver, self.url.clone()).await?)
40    }
41    fn recycle(
42        &self,
43        _: &mut Self::Type,
44        _: &Metrics,
45    ) -> impl Future<Output = RecycleResult<Self::Error>> + Send {
46        future::ready(RecycleResult::Ok(()))
47    }
48}
49
50/// A database connection borrowed from a [`ConnectionPool`].
51///
52/// Implements both [`Executor`] and [`Connection`], so it can be used anywhere
53/// a plain connection is expected.  Deref-coerces to the underlying driver
54/// connection (`D::Connection`) via [`Deref`]/[`DerefMut`], giving access to
55/// any driver-specific extension methods.
56///
57/// The borrowed connection is automatically returned to the pool when this
58/// value is dropped. If you need to take full ownership of the connection
59/// outside pool management, call [`ConnectionPool::detach`] instead.
60#[derive(Debug)]
61pub struct PooledConnection<D: Driver> {
62    pub(crate) object: Object<DBConnectionManager<D>>,
63}
64
65/// A managed pool of reusable database connections.
66///
67/// Every method that yields connections produces a [`PooledConnection`]
68/// that is automatically returned to the pool on drop, keeping the number
69/// of open database connections bounded.
70pub trait ConnectionPool<D: Driver>: Sync + Send + Debug {
71    /// Acquires a connection from the pool.
72    ///
73    /// If all connections are in use and the pool is at its maximum size, this
74    /// call waits until one becomes available or the configured
75    /// [`PoolConfig::wait_timeout`] elapses, at which point an error is
76    /// returned.
77    fn get<'s>(&'s self) -> BoxFuture<'s, Result<PooledConnection<D>>>;
78
79    /// Acquires a connection from the pool with an explicit timeout.
80    ///
81    /// Identical to [`get`](ConnectionPool::get) but overrides the configured
82    /// wait timeout with `timeout`.  Useful when individual call sites need
83    /// stricter or looser deadlines than the pool-level default.
84    fn timeout_get<'s>(&'s self, timeout: Duration) -> BoxFuture<'s, Result<PooledConnection<D>>>;
85
86    /// Acquires a connection and removes it from pool management.
87    ///
88    /// The returned `D::Connection` is a plain, pool-unaware connection.  It
89    /// will **not** be returned to the pool when dropped, the underlying
90    /// database connection is closed instead. Use this when you need to hand
91    /// a connection to code that does not know about the pool, or when you
92    /// want to guarantee the connection is fully closed rather than recycled.
93    fn detach<'s>(&'s self) -> BoxFuture<'s, Result<D::Connection>>;
94
95    /// Changes the maximum number of connections the pool may hold.
96    fn resize(&self, max_size: usize) -> Result<()>;
97
98    /// Converts this pool into a sized type-erased `Box<dyn ConnectionPool<D>>`.
99    fn into_box(self) -> Box<dyn ConnectionPool<D> + Send + Sync>
100    where
101        Self: Sized,
102        D: 'static;
103
104    /// Converts this pool into a sized type-erased `Arc<dyn ConnectionPool<D>>`.
105    fn into_arc(self) -> Arc<dyn ConnectionPool<D> + Send + Sync>
106    where
107        Self: Sized,
108        D: 'static;
109
110    /// Closes the pool, marking it as unavailable and dropping all managed
111    /// connections.
112    fn close(self) -> BoxFuture<'static, Result<()>>;
113}
114
115impl<D: Driver> ConnectionPool<D> for Pool<DBConnectionManager<D>>
116where
117    <D as Driver>::Connection: Debug,
118{
119    fn get<'s>(&'s self) -> BoxFuture<'s, Result<PooledConnection<D>>> {
120        async move {
121            let object = Pool::<DBConnectionManager<D>>::get(self)
122                .await
123                .map_err(|e| anyhow!("{e:#?}"))?;
124            Ok(PooledConnection { object })
125        }
126        .boxed()
127    }
128
129    fn timeout_get<'s>(&'s self, timeout: Duration) -> BoxFuture<'s, Result<PooledConnection<D>>> {
130        async move {
131            let object = Pool::<DBConnectionManager<D>>::timeout_get(
132                self,
133                &Timeouts::wait_millis(timeout.as_millis() as u64),
134            )
135            .await
136            .map_err(|e| anyhow!("{e:#?}"))?;
137            Ok(PooledConnection::<D> { object })
138        }
139        .boxed()
140    }
141
142    fn detach<'s>(&'s self) -> BoxFuture<'s, Result<D::Connection>>
143    where
144        Self: Sized,
145    {
146        async {
147            let v = Pool::<DBConnectionManager<D>>::get(self)
148                .await
149                .map_err(|e| anyhow!("{e:#?}"))?;
150            Ok(Object::<DBConnectionManager<D>>::take(v))
151        }
152        .boxed()
153    }
154
155    fn resize(&self, max_size: usize) -> Result<()> {
156        Ok(self.resize(max_size))
157    }
158
159    fn into_box(self) -> Box<dyn ConnectionPool<D> + Send + Sync>
160    where
161        Self: Sized,
162        D: 'static,
163    {
164        Box::new(self)
165    }
166
167    fn into_arc(self) -> Arc<dyn ConnectionPool<D> + Send + Sync>
168    where
169        Self: Sized,
170        D: 'static,
171    {
172        Arc::new(self)
173    }
174
175    fn close(self) -> BoxFuture<'static, Result<()>> {
176        Self::close(&self);
177        future::ready(Ok(())).boxed()
178    }
179}
180
181impl<D: Driver> Executor for PooledConnection<D> {
182    type Driver = D;
183
184    fn accepts_multiple_statements(&self) -> bool {
185        self.object.accepts_multiple_statements()
186    }
187
188    fn driver(&self) -> D {
189        self.object.driver()
190    }
191
192    fn prepare<'s>(
193        &'s mut self,
194        query: impl AsQuery<D> + 's,
195    ) -> impl Future<Output = Result<Query<D>>> + Send {
196        self.object.prepare(query)
197    }
198
199    fn do_prepare(&mut self, sql: String) -> impl Future<Output = Result<Query<D>>> + Send {
200        self.object.do_prepare(sql)
201    }
202
203    fn run<'s>(
204        &'s mut self,
205        query: impl AsQuery<D> + 's,
206    ) -> impl Stream<Item = Result<QueryResult>> + Send {
207        self.object.run(query)
208    }
209
210    fn fetch<'s>(
211        &'s mut self,
212        query: impl AsQuery<D> + 's,
213    ) -> impl Stream<Item = Result<Row>> + Send {
214        self.object.fetch(query)
215    }
216
217    fn execute<'s>(
218        &'s mut self,
219        query: impl AsQuery<D> + 's,
220    ) -> impl Future<Output = Result<RowsAffected>> + Send {
221        self.object.execute(query)
222    }
223
224    fn append<It>(&mut self, entities: It) -> impl Future<Output = Result<RowsAffected>> + Send
225    where
226        It: IntoIterator + Send,
227        It::IntoIter: Send,
228        It::Item: AsEntity,
229    {
230        self.object.append(entities)
231    }
232}
233
234impl<D: Driver> Connection for PooledConnection<D> {
235    fn connect(
236        _driver: &D,
237        _url: Cow<'static, str>,
238    ) -> impl Future<Output = Result<<D as Driver>::Connection>> + Send
239    where
240        Self: Sized,
241    {
242        future::ready(Err(anyhow!(
243            "Cannot connect using a PooledConnection, such object must be obtained from a connection pool",
244        )))
245    }
246
247    fn begin(&mut self) -> impl Future<Output = Result<<D as Driver>::Transaction<'_>>> + Send {
248        self.object.begin()
249    }
250}
251
252impl<D: Driver> Deref for PooledConnection<D> {
253    type Target = D::Connection;
254    fn deref(&self) -> &Self::Target {
255        &self.object
256    }
257}
258
259impl<D: Driver> DerefMut for PooledConnection<D> {
260    fn deref_mut(&mut self) -> &mut Self::Target {
261        &mut self.object
262    }
263}
264
265impl<D: Driver> AsRef<D::Connection> for PooledConnection<D> {
266    fn as_ref(&self) -> &D::Connection {
267        &self.object
268    }
269}
270
271impl<D: Driver> AsMut<D::Connection> for PooledConnection<D> {
272    fn as_mut(&mut self) -> &mut D::Connection {
273        &mut self.object
274    }
275}