sqlx_pool_registry/provider.rs
1use crate::sqlx::PgPool;
2
3/// Trait for providing database pools with read/write routing.
4///
5/// Implementations can provide separate read and write pools for load distribution,
6/// or use a single pool for both operations.
7///
8/// # Thread Safety
9///
10/// Implementations must be `Clone`, `Send`, and `Sync` to work with async Rust
11/// and be shared across tasks.
12///
13/// # When to Use Each Method
14///
15/// ## `.read()` - For Read Operations
16///
17/// Use for queries that:
18/// - Don't modify data (SELECT without FOR UPDATE)
19/// - Can tolerate slight staleness (eventual consistency)
20/// - Benefit from load distribution
21///
22/// Examples: user listings, analytics, dashboards, search
23///
24/// ## `.write()` - For Write Operations
25///
26/// Use for operations that:
27/// - Modify data (INSERT, UPDATE, DELETE)
28/// - Require transactions
29/// - Need locking reads (SELECT FOR UPDATE)
30/// - Require read-after-write consistency
31///
32/// Examples: creating records, updates, deletes, transactions
33///
34/// # Example Implementation
35///
36/// ```
37/// use sqlx_pool_registry::sqlx::{self, PgPool};
38/// use sqlx_pool_registry::PoolProvider;
39///
40/// #[derive(Clone)]
41/// struct MyPools {
42/// primary: PgPool,
43/// replica: Option<PgPool>,
44/// }
45///
46/// impl PoolProvider for MyPools {
47/// fn read(&self) -> &PgPool {
48/// self.replica.as_ref().unwrap_or(&self.primary)
49/// }
50///
51/// fn write(&self) -> &PgPool {
52/// &self.primary
53/// }
54/// }
55/// ```
56pub trait PoolProvider: Clone + Send + Sync + 'static {
57 /// Get a pool for read operations.
58 ///
59 /// May return a read replica for load distribution, or fall back to
60 /// the primary pool if no replica is configured.
61 fn read(&self) -> &PgPool;
62
63 /// Get a pool for write operations.
64 ///
65 /// Should return the primary pool for operations that require writes,
66 /// locking reads, or read-after-write consistency.
67 fn write(&self) -> &PgPool;
68}
69
70/// Implement PoolProvider for PgPool for backward compatibility.
71///
72/// This allows existing code using `PgPool` directly to work with generic
73/// code that accepts `impl PoolProvider` without any changes.
74///
75/// # Example
76///
77/// ```rust,no_run
78/// use sqlx_pool_registry::sqlx::{self, PgPool};
79/// use sqlx_pool_registry::PoolProvider;
80///
81/// async fn query_user<P: PoolProvider>(pools: &P, id: i64) -> Result<String, sqlx::Error> {
82/// sqlx::query_scalar("SELECT name FROM users WHERE id = $1")
83/// .bind(id)
84/// .fetch_one(pools.read())
85/// .await
86/// }
87///
88/// # async fn example() -> Result<(), sqlx::Error> {
89/// let pool = PgPool::connect("postgresql://localhost/db").await?;
90///
91/// // Works with PgPool directly
92/// let name = query_user(&pool, 1).await?;
93/// # Ok(())
94/// # }
95/// ```
96impl PoolProvider for PgPool {
97 fn read(&self) -> &PgPool {
98 self
99 }
100
101 fn write(&self) -> &PgPool {
102 self
103 }
104}