1use crate::RullstPool;
2use std::sync::atomic::{AtomicUsize, Ordering};
3
4pub struct ReplicaPool {
7 primary: RullstPool,
8 replicas: Vec<RullstPool>,
9 current_replica: AtomicUsize,
10}
11
12impl ReplicaPool {
13 pub fn new(primary: RullstPool, replicas: Vec<RullstPool>) -> Self {
15 Self {
16 primary,
17 replicas,
18 current_replica: AtomicUsize::new(0),
19 }
20 }
21
22 pub fn primary_only(primary: RullstPool) -> Self {
24 Self::new(primary, Vec::new())
25 }
26
27 pub fn read(&self) -> &RullstPool {
30 if self.replicas.is_empty() {
31 &self.primary
32 } else {
33 let idx = self.current_replica.fetch_add(1, Ordering::Relaxed) % self.replicas.len();
34 &self.replicas[idx]
35 }
36 }
37
38 pub fn write(&self) -> &RullstPool {
40 &self.primary
41 }
42
43 pub fn replica_count(&self) -> usize {
45 self.replicas.len()
46 }
47}
48
49#[cfg(test)]
50#[allow(unused_imports)]
51mod tests {
52 use super::*;
53
54 #[tokio::test]
55 async fn test_replica_pool_count_and_rotation() {
56 #[cfg(not(any(
57 feature = "strict-postgres",
58 feature = "strict-mysql",
59 feature = "strict-sqlite"
60 )))]
61 {
62 sqlx::any::install_default_drivers();
63 }
64
65 #[cfg(not(any(feature = "strict-postgres", feature = "strict-mysql")))]
66 {
67 if let (Ok(pool), Ok(replica1), Ok(replica2)) = (
68 RullstPool::connect("sqlite::memory:").await,
69 RullstPool::connect("sqlite::memory:").await,
70 RullstPool::connect("sqlite::memory:").await,
71 ) {
72 let replica_pool = ReplicaPool::new(pool.clone(), vec![replica1, replica2]);
73 assert_eq!(replica_pool.replica_count(), 2);
74
75 let _r1 = replica_pool.read();
77 let _r2 = replica_pool.read();
78 let _r3 = replica_pool.read();
79
80 let primary_only = ReplicaPool::primary_only(pool);
81 assert_eq!(primary_only.replica_count(), 0);
82 let _p_read = primary_only.read();
83 }
84 }
85 }
86}