use std::sync::Arc;
use tokio::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
use crate::{BoxStrategy, error::Error, pool::Pool};
pub(crate) struct Accessor {
pool: RwLock<Pool>,
strategy: BoxStrategy,
}
impl Accessor {
pub(crate) fn new(pool: Pool, strategy: BoxStrategy) -> Self {
Self {
pool: RwLock::new(pool),
strategy,
}
}
#[inline(always)]
pub(crate) async fn read(&self) -> RwLockReadGuard<'_, Pool> {
self.pool.read().await
}
#[inline(always)]
pub(crate) async fn write(&self) -> RwLockWriteGuard<'_, Pool> {
self.pool.write().await
}
pub(crate) async fn route(
&self,
tier: &str,
bucket_id: u64,
on_master: bool,
) -> Result<Arc<pgpool::Pool>, Error> {
let guard = self.pool.read().await;
if let Some(pool) = guard.resolve(tier, bucket_id, on_master) {
return Ok(pool);
}
drop(guard);
log::debug!("route: bucket_id={bucket_id} not resolved, fallback round-robin");
self.next().await
}
pub(crate) async fn next(&self) -> Result<Arc<pgpool::Pool>, Error> {
let guard = self.pool.read().await;
let index = self.strategy.next(guard.active_count());
guard.pick(index).ok_or(Error::Unreachable)
}
#[inline(always)]
pub(crate) async fn map<C, F>(&self, c: C) -> Result<(), Error>
where
F: Future<Output = Result<(), Error>>,
C: Fn(Arc<pgpool::Pool>) -> F,
{
let guard = self.pool.read().await;
for pool in guard.active_pools() {
c(pool.clone()).await?;
}
Ok(())
}
pub(crate) async fn close(&self) {
self.pool.write().await.close();
}
}