Skip to main content

boson_backend_sqlite/
lib.rs

1//! `SQLite` [`QueueBackend`](boson_core::QueueBackend) for Boson.
2//!
3//! **When to use:** durable single-host Mode 1, or Mode 2 on one machine when enqueue and worker
4//! processes share the same database file (`BOSON_SQLITE_PATH`). Enable via the `boson` crate
5//! `sqlite` feature.
6//!
7//! Getting started:
8//! [Mode 1](https://docs.rs/uf-boson/latest/boson/index.html#mode-1--embedded-one-binary) /
9//! [Mode 2](https://docs.rs/uf-boson/latest/boson/index.html#mode-2--remote-worker-two-binaries).
10//!
11//! ## Entry points
12//!
13//! - [`SqliteQueueBackend::new`] / [`SqliteQueueBackend::connect`] — open a database
14//! - [`install_default_sqlite_backend`] — register on the global [`QueueRouter`](boson_core::QueueRouter)
15//!
16//! ## Mode 2 — Enqueue binary
17//!
18//! Shared file path with the worker. No claim loop in this process:
19//!
20//! ```rust,no_run
21//! use std::sync::Arc;
22//!
23//! use boson_backend_sqlite::SqliteQueueBackend;
24//! use boson_core::JsonExecutionContextFactory;
25//! use boson_runtime::{configure, Boson};
26//!
27//! # async fn boot_enqueue() -> boson_core::Result<()> {
28//! let path = std::env::var("BOSON_SQLITE_PATH").unwrap_or_else(|_| "/tmp/boson-remote.db".into());
29//! let backend = SqliteQueueBackend::new(&path).await?;
30//! let boson = Boson::builder()
31//!     .queue_backend(Arc::new(backend))
32//!     .execution_context_factory(JsonExecutionContextFactory)
33//!     .auto_registry()
34//!     .without_worker()
35//!     .build()?;
36//! configure(boson);
37//! // MyTask::send_with(...).await?;
38//! # Ok(())
39//! # }
40//! ```
41//!
42//! Runnable: `cargo run -p uf-boson --example remote_enqueue --features sqlite`
43//!
44//! ## Mode 2 — Worker binary
45//!
46//! Same `BOSON_SQLITE_PATH`, unique `worker_id`, and `lease_ttl_secs > 0`:
47//!
48//! ```rust,no_run
49//! use std::sync::Arc;
50//!
51//! use boson_backend_sqlite::SqliteQueueBackend;
52//! use boson_core::JsonExecutionContextFactory;
53//! use boson_runtime::Boson;
54//!
55//! # async fn boot_worker() -> boson_core::Result<()> {
56//! let path = std::env::var("BOSON_SQLITE_PATH").unwrap_or_else(|_| "/tmp/boson-remote.db".into());
57//! let backend = SqliteQueueBackend::new(&path).await?;
58//! let _boson = Boson::builder()
59//!     .queue_backend(Arc::new(backend))
60//!     .execution_context_factory(JsonExecutionContextFactory)
61//!     .worker_id(std::env::var("BOSON_WORKER_ID").unwrap_or_else(|_| "worker-1".into()))
62//!     .lease_ttl_secs(30)
63//!     .auto_registry()
64//!     .build()?;
65//! # Ok(())
66//! # }
67//! ```
68//!
69//! Runnable: `cargo run -p uf-boson --example remote_worker --features sqlite`
70//!
71//! Other Mode 2 backends:
72//! [Postgres](../boson_backend_postgres/index.html#mode-2--enqueue-binary),
73//! [Redis](../boson_backend_redis/index.html#mode-2--enqueue-binary),
74//! [NATS](../boson_backend_nats/index.html#mode-2--enqueue-binary).
75
76mod bootstrap;
77
78use std::path::Path;
79
80use boson_backend_sql_common::SqlQueueBackend;
81use boson_core::Result;
82use sqlx::SqlitePool;
83
84pub use bootstrap::install_default_sqlite_backend;
85
86/// `SQLite`-backed queue backend.
87///
88/// Suitable for Mode 1 embedded boots and for Mode 2 when both binaries open the **same path**.
89/// For multi-host fleets prefer Postgres, Redis, or NATS.
90///
91/// Mode 2 examples: [enqueue](index.html#mode-2--enqueue-binary) /
92/// [worker](index.html#mode-2--worker-binary).
93pub struct SqliteQueueBackend {
94    inner: SqlQueueBackend,
95}
96
97impl SqliteQueueBackend {
98    /// Open a `SQLite` database at `path` (creates the file if missing).
99    ///
100    /// See crate-level [Mode 2 — Enqueue binary](index.html#mode-2--enqueue-binary) and
101    /// [Mode 2 — Worker binary](index.html#mode-2--worker-binary).
102    ///
103    /// # Examples
104    ///
105    /// ```rust,no_run
106    /// use std::sync::Arc;
107    ///
108    /// use boson_backend_sqlite::SqliteQueueBackend;
109    /// use boson_core::JsonExecutionContextFactory;
110    /// use boson_runtime::Boson;
111    ///
112    /// # async fn boot() -> boson_core::Result<()> {
113    /// let path = std::env::var("BOSON_SQLITE_PATH").unwrap_or_else(|_| "/tmp/boson.db".into());
114    /// let backend = SqliteQueueBackend::new(&path).await?;
115    /// let _boson = Boson::builder()
116    ///     .queue_backend(Arc::new(backend))
117    ///     .execution_context_factory(JsonExecutionContextFactory)
118    ///     .auto_registry()
119    ///     .build()?;
120    /// # Ok(())
121    /// # }
122    /// ```
123    ///
124    /// # Errors
125    ///
126    /// Returns an error when the database cannot be opened or schema bootstrap fails.
127    pub async fn new(path: impl AsRef<Path>) -> Result<Self> {
128        let url = format!("sqlite://{}?mode=rwc", path.as_ref().display());
129        Self::connect(&url).await
130    }
131
132    /// Connect using a `SQLite` connection URL.
133    ///
134    /// # Errors
135    ///
136    /// Returns an error when the pool cannot connect or schema bootstrap fails.
137    pub async fn connect(url: &str) -> Result<Self> {
138        let inner = SqlQueueBackend::connect_sqlite(url).await?;
139        Ok(Self { inner })
140    }
141
142    /// Wrap an existing pool (schema bootstrap runs).
143    ///
144    /// # Errors
145    ///
146    /// Returns an error when schema bootstrap fails.
147    pub async fn from_pool(pool: SqlitePool) -> Result<Self> {
148        let inner = SqlQueueBackend::from_sqlite_pool(pool).await?;
149        Ok(Self { inner })
150    }
151
152    /// Underlying connection pool.
153    ///
154    /// # Panics
155    ///
156    /// Panics if the inner pool is not `SQLite` (internal invariant violation).
157    #[must_use]
158    pub fn pool(&self) -> &SqlitePool {
159        match self.inner.pool() {
160            boson_backend_sql_common::SqlPool::Sqlite(pool) => pool,
161            boson_backend_sql_common::SqlPool::Postgres(_) => {
162                panic!("sqlite backend has non-sqlite pool")
163            }
164        }
165    }
166}
167
168impl std::fmt::Debug for SqliteQueueBackend {
169    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
170        f.debug_struct("SqliteQueueBackend").finish_non_exhaustive()
171    }
172}
173
174boson_backend_sql_common::delegate_queue_backend!(SqliteQueueBackend, inner);