use crate::RullstPool;
use std::sync::atomic::{AtomicUsize, Ordering};
pub struct ReplicaPool {
primary: RullstPool,
replicas: Vec<RullstPool>,
current_replica: AtomicUsize,
}
impl ReplicaPool {
pub fn new(primary: RullstPool, replicas: Vec<RullstPool>) -> Self {
Self {
primary,
replicas,
current_replica: AtomicUsize::new(0),
}
}
pub fn primary_only(primary: RullstPool) -> Self {
Self::new(primary, Vec::new())
}
pub fn read(&self) -> &RullstPool {
if self.replicas.is_empty() {
&self.primary
} else {
let idx = self.current_replica.fetch_add(1, Ordering::Relaxed) % self.replicas.len();
&self.replicas[idx]
}
}
pub fn write(&self) -> &RullstPool {
&self.primary
}
pub fn replica_count(&self) -> usize {
self.replicas.len()
}
}
#[cfg(test)]
#[allow(unused_imports)]
mod tests {
use super::*;
#[tokio::test]
async fn test_replica_pool_count_and_rotation() {
#[cfg(not(any(
feature = "strict-postgres",
feature = "strict-mysql",
feature = "strict-sqlite"
)))]
{
sqlx::any::install_default_drivers();
}
#[cfg(not(any(feature = "strict-postgres", feature = "strict-mysql")))]
{
if let (Ok(pool), Ok(replica1), Ok(replica2)) = (
RullstPool::connect("sqlite::memory:").await,
RullstPool::connect("sqlite::memory:").await,
RullstPool::connect("sqlite::memory:").await,
) {
let replica_pool = ReplicaPool::new(pool.clone(), vec![replica1, replica2]);
assert_eq!(replica_pool.replica_count(), 2);
let _r1 = replica_pool.read();
let _r2 = replica_pool.read();
let _r3 = replica_pool.read();
let primary_only = ReplicaPool::primary_only(pool);
assert_eq!(primary_only.replica_count(), 0);
let _p_read = primary_only.read();
}
}
}
}