nntp_proxy/router/
backend_info.rs1use derive_more::{AsRef, Deref, Display, From};
7use std::cmp::Ordering as CmpOrdering;
8use std::sync::Arc;
9use std::sync::atomic::{AtomicUsize, Ordering};
10
11use crate::pool::DeadpoolConnectionProvider;
12use crate::types::{BackendId, ServerName};
13
14#[derive(Debug, Clone, Copy, PartialEq, Display, From, AsRef, Deref)]
18pub struct LoadRatio(f64);
19
20impl LoadRatio {
21 pub const MAX: Self = Self(f64::MAX);
23
24 pub const MIN: Self = Self(0.0);
26
27 #[inline]
29 #[must_use]
30 pub const fn new(ratio: f64) -> Self {
31 Self(ratio)
32 }
33
34 #[inline]
36 #[must_use]
37 pub const fn get(&self) -> f64 {
38 self.0
39 }
40}
41
42impl PartialOrd for LoadRatio {
43 fn partial_cmp(&self, other: &Self) -> Option<CmpOrdering> {
44 self.0.partial_cmp(&other.0)
45 }
46}
47
48#[derive(Debug, Clone, Display, From, AsRef, Deref)]
50#[display("PendingCount({})", "_0.load(Ordering::Relaxed)")]
51pub struct PendingCount(Arc<AtomicUsize>);
52
53impl PartialEq for PendingCount {
55 fn eq(&self, other: &Self) -> bool {
56 self.get() == other.get()
57 }
58}
59
60impl PartialEq<usize> for PendingCount {
61 fn eq(&self, other: &usize) -> bool {
62 self.get() == *other
63 }
64}
65
66impl Eq for PendingCount {}
67
68impl PendingCount {
69 #[inline]
71 #[must_use]
72 pub fn new() -> Self {
73 Self(Arc::new(AtomicUsize::new(0)))
74 }
75
76 #[inline]
78 pub fn increment(&self) {
79 self.0.fetch_add(1, Ordering::Relaxed);
80 }
81
82 #[inline]
84 #[must_use]
85 pub fn try_increment_from(&self, observed: usize) -> bool {
86 self.0
87 .compare_exchange(observed, observed + 1, Ordering::AcqRel, Ordering::Acquire)
88 .is_ok()
89 }
90
91 #[inline]
93 pub fn decrement(&self) {
94 self.0.fetch_sub(1, Ordering::Relaxed);
95 }
96
97 #[inline]
99 #[must_use]
100 pub fn get(&self) -> usize {
101 self.0.load(Ordering::Relaxed)
102 }
103}
104
105impl Default for PendingCount {
106 fn default() -> Self {
107 Self::new()
108 }
109}
110
111#[derive(Debug, Clone, Display, From, AsRef, Deref)]
113#[display("StatefulCount({})", "_0.load(Ordering::Relaxed)")]
114pub struct StatefulCount(Arc<AtomicUsize>);
115
116impl PartialEq for StatefulCount {
118 fn eq(&self, other: &Self) -> bool {
119 self.get() == other.get()
120 }
121}
122
123impl PartialEq<usize> for StatefulCount {
124 fn eq(&self, other: &usize) -> bool {
125 self.get() == *other
126 }
127}
128
129impl Eq for StatefulCount {}
130
131impl StatefulCount {
132 #[inline]
134 #[must_use]
135 pub fn new() -> Self {
136 Self(Arc::new(AtomicUsize::new(0)))
137 }
138
139 #[inline]
141 #[must_use]
142 pub fn get(&self) -> usize {
143 self.0.load(Ordering::Relaxed)
144 }
145
146 #[must_use]
150 pub fn try_acquire(&self, max_stateful: usize) -> bool {
151 let mut current = self.0.load(Ordering::Acquire);
152 loop {
153 if current >= max_stateful {
154 return false;
155 }
156
157 match self.0.compare_exchange_weak(
158 current,
159 current + 1,
160 Ordering::AcqRel,
161 Ordering::Acquire,
162 ) {
163 Ok(_) => return true,
164 Err(actual) => current = actual,
165 }
166 }
167 }
168
169 pub fn release(&self) -> Result<usize, usize> {
176 self.0
177 .fetch_update(Ordering::AcqRel, Ordering::Acquire, |current| {
178 if current == 0 {
179 None
180 } else {
181 Some(current - 1)
182 }
183 })
184 }
185}
186
187impl Default for StatefulCount {
188 fn default() -> Self {
189 Self::new()
190 }
191}
192
193#[derive(Debug, Clone)]
195pub(super) struct BackendInfo {
196 pub(super) id: BackendId,
198 pub(super) name: ServerName,
200 pub(super) provider: DeadpoolConnectionProvider,
202 pub(super) pending_count: PendingCount,
204 pub(super) stateful_count: StatefulCount,
206 pub(super) tier: u8,
208}
209
210impl BackendInfo {
211 #[must_use]
215 pub(super) fn load_ratio(&self) -> LoadRatio {
216 #[allow(clippy::cast_precision_loss)]
219 let status = self.provider.status_counts();
222 let max_conns = status.max_size as f64;
223 if max_conns > 0.0 {
224 let checked_out = status.size.saturating_sub(status.available);
225 #[allow(clippy::cast_precision_loss)]
226 let active = self.pending_count.get().max(checked_out) as f64;
228 LoadRatio::new(active / max_conns)
229 } else {
230 LoadRatio::MAX
231 }
232 }
233}