Skip to main content

server/user/
connection.rs

1//! A tenant connection that keeps its pool alive.
2
3use crate::user::userpool::registry::TenantPool;
4use sqlx::pool::PoolConnection;
5use sqlx::{PgConnection, Postgres};
6use std::ops::{Deref, DerefMut};
7use std::sync::Arc;
8
9/// A checked-out connection, holding the tenant pool it came from.
10///
11/// The anchor is what makes the residency bound true. A permit lives inside
12/// [`TenantPool`] and is returned when the last handle drops, so handing back a
13/// bare `PoolConnection` released the permit while the connection was still
14/// open — sqlx keeps the pool alive behind the connection, so the process could
15/// then admit a ninth resident pool and exceed the budget it claims to enforce.
16///
17/// Dereferences to [`PgConnection`], so `&mut *conn` and `&mut conn` reach an
18/// executor exactly as they did before.
19pub struct TenantConnection {
20    conn: PoolConnection<Postgres>,
21    /// `None` for the global/admin pool, which is bounded by its own cap rather
22    /// than by residency permits.
23    _anchor: Option<Arc<TenantPool>>,
24}
25
26impl TenantConnection {
27    /// Only the tenant-routing half builds these, and that half is excluded
28    /// from server's own `cfg(test)` runs.
29    #[cfg(not(test))]
30    pub(crate) const fn tenant(conn: PoolConnection<Postgres>, anchor: Arc<TenantPool>) -> Self {
31        Self {
32            conn,
33            _anchor: Some(anchor),
34        }
35    }
36
37    /// Only the test and `test-utils` arms of `User::get_connection` route a
38    /// global connection through the tenant type; production always has a pool.
39    #[cfg(any(test, feature = "test-utils"))]
40    pub(crate) const fn global(conn: PoolConnection<Postgres>) -> Self {
41        Self {
42            conn,
43            _anchor: None,
44        }
45    }
46}
47
48impl Deref for TenantConnection {
49    type Target = PgConnection;
50
51    fn deref(&self) -> &Self::Target {
52        &self.conn
53    }
54}
55
56impl DerefMut for TenantConnection {
57    fn deref_mut(&mut self) -> &mut Self::Target {
58        &mut self.conn
59    }
60}