Skip to main content

boson_backend_postgres/
lib.rs

1//! `PostgreSQL` [`QueueBackend`](boson_core::QueueBackend) for Boson.
2//!
3//! Choose at worker boot. Enable via the `boson` crate `postgres` feature.
4//!
5//! ## Entry points
6//!
7//! - [`PostgresQueueBackend::connect`] — open a pool and bootstrap schema
8//! - [`install_default_postgres_backend`] — register on the global [`QueueRouter`](boson_core::QueueRouter)
9
10mod bootstrap;
11
12use boson_backend_sql_common::SqlQueueBackend;
13use boson_core::Result;
14use sqlx::PgPool;
15
16pub use bootstrap::{
17    install_default_postgres_backend, install_isolated_postgres_backend, postgres_test_url,
18};
19
20/// PostgreSQL-backed queue backend.
21pub struct PostgresQueueBackend {
22    inner: SqlQueueBackend,
23}
24
25impl PostgresQueueBackend {
26    /// Connect to `PostgreSQL` at `url`.
27    ///
28    /// # Errors
29    ///
30    /// Returns an error when the pool cannot connect or schema bootstrap fails.
31    pub async fn new(url: &str) -> Result<Self> {
32        Self::connect(url).await
33    }
34
35    /// Connect using a `PostgreSQL` connection URL.
36    ///
37    /// # Examples
38    ///
39    /// ```rust,no_run
40    /// use boson_backend_postgres::PostgresQueueBackend;
41    ///
42    /// # async fn connect() -> boson_core::Result<()> {
43    /// let backend = PostgresQueueBackend::connect("postgres://localhost/boson").await?;
44    /// # Ok(())
45    /// # }
46    /// ```
47    ///
48    /// # Errors
49    ///
50    /// Returns an error when the pool cannot connect or schema bootstrap fails.
51    pub async fn connect(url: &str) -> Result<Self> {
52        let inner = SqlQueueBackend::connect_postgres(url).await?;
53        Ok(Self { inner })
54    }
55
56    /// Connect with an isolated schema (for parallel tests).
57    ///
58    /// # Errors
59    ///
60    /// Returns an error when schema creation, pool connect, or bootstrap fails.
61    pub async fn connect_isolated(url: &str, schema: &str) -> Result<Self> {
62        let inner = SqlQueueBackend::connect_postgres_isolated(url, schema).await?;
63        Ok(Self { inner })
64    }
65
66    /// Wrap an existing pool (schema bootstrap runs).
67    ///
68    /// # Errors
69    ///
70    /// Returns an error when schema bootstrap fails.
71    pub async fn from_pool(pool: PgPool) -> Result<Self> {
72        let inner = SqlQueueBackend::from_postgres_pool(pool).await?;
73        Ok(Self { inner })
74    }
75
76    /// Underlying connection pool.
77    ///
78    /// # Panics
79    ///
80    /// Panics if the inner pool is not `PostgreSQL` (internal invariant violation).
81    #[must_use]
82    pub fn pool(&self) -> &PgPool {
83        match self.inner.pool() {
84            boson_backend_sql_common::SqlPool::Postgres(pool) => pool,
85            boson_backend_sql_common::SqlPool::Sqlite(_) => {
86                panic!("postgres backend has non-postgres pool")
87            }
88        }
89    }
90}
91
92impl std::fmt::Debug for PostgresQueueBackend {
93    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
94        f.debug_struct("PostgresQueueBackend").finish_non_exhaustive()
95    }
96}
97
98boson_backend_sql_common::delegate_queue_backend!(PostgresQueueBackend, inner);