Skip to main content

tank_core/
pool.rs

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