use derive_more::{AsRef, Deref, Display, From};
use std::cmp::Ordering as CmpOrdering;
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use crate::pool::DeadpoolConnectionProvider;
use crate::types::{BackendId, ServerName};
#[derive(Debug, Clone, Copy, PartialEq, Display, From, AsRef, Deref)]
pub struct LoadRatio(f64);
impl LoadRatio {
pub const MAX: Self = Self(f64::MAX);
pub const MIN: Self = Self(0.0);
#[inline]
#[must_use]
pub const fn new(ratio: f64) -> Self {
Self(ratio)
}
#[inline]
#[must_use]
pub const fn get(&self) -> f64 {
self.0
}
}
impl PartialOrd for LoadRatio {
fn partial_cmp(&self, other: &Self) -> Option<CmpOrdering> {
self.0.partial_cmp(&other.0)
}
}
#[derive(Debug, Clone, Display, From, AsRef, Deref)]
#[display("PendingCount({})", "_0.load(Ordering::Relaxed)")]
pub struct PendingCount(Arc<AtomicUsize>);
impl PartialEq for PendingCount {
fn eq(&self, other: &Self) -> bool {
self.get() == other.get()
}
}
impl PartialEq<usize> for PendingCount {
fn eq(&self, other: &usize) -> bool {
self.get() == *other
}
}
impl Eq for PendingCount {}
impl PendingCount {
#[inline]
#[must_use]
pub fn new() -> Self {
Self(Arc::new(AtomicUsize::new(0)))
}
#[inline]
pub fn increment(&self) {
self.0.fetch_add(1, Ordering::Relaxed);
}
#[inline]
pub fn decrement(&self) {
self.0.fetch_sub(1, Ordering::Relaxed);
}
#[inline]
#[must_use]
pub fn get(&self) -> usize {
self.0.load(Ordering::Relaxed)
}
}
impl Default for PendingCount {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Display, From, AsRef, Deref)]
#[display("StatefulCount({})", "_0.load(Ordering::Relaxed)")]
pub struct StatefulCount(Arc<AtomicUsize>);
impl PartialEq for StatefulCount {
fn eq(&self, other: &Self) -> bool {
self.get() == other.get()
}
}
impl PartialEq<usize> for StatefulCount {
fn eq(&self, other: &usize) -> bool {
self.get() == *other
}
}
impl Eq for StatefulCount {}
impl StatefulCount {
#[inline]
#[must_use]
pub fn new() -> Self {
Self(Arc::new(AtomicUsize::new(0)))
}
#[inline]
#[must_use]
pub fn get(&self) -> usize {
self.0.load(Ordering::Relaxed)
}
#[must_use]
pub fn try_acquire(&self, max_stateful: usize) -> bool {
let mut current = self.0.load(Ordering::Acquire);
loop {
if current >= max_stateful {
return false;
}
match self.0.compare_exchange_weak(
current,
current + 1,
Ordering::AcqRel,
Ordering::Acquire,
) {
Ok(_) => return true,
Err(actual) => current = actual,
}
}
}
pub fn release(&self) -> Result<usize, usize> {
self.0
.fetch_update(Ordering::AcqRel, Ordering::Acquire, |current| {
if current == 0 {
None
} else {
Some(current - 1)
}
})
}
}
impl Default for StatefulCount {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub(super) struct BackendInfo {
pub(super) id: BackendId,
pub(super) name: ServerName,
pub(super) provider: DeadpoolConnectionProvider,
pub(super) pending_count: PendingCount,
pub(super) stateful_count: StatefulCount,
pub(super) tier: u8,
}
impl BackendInfo {
#[must_use]
pub(super) fn load_ratio(&self) -> LoadRatio {
#[allow(clippy::cast_precision_loss)]
let status = self.provider.status_counts();
let max_conns = status.max_size as f64;
if max_conns > 0.0 {
let checked_out = status.size.saturating_sub(status.available);
#[allow(clippy::cast_precision_loss)]
let active = self.pending_count.get().max(checked_out) as f64;
LoadRatio::new(active / max_conns)
} else {
LoadRatio::MAX
}
}
}