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