load_balancer/
round_robin.rs1use crate::LoadBalancer;
2use std::future::Future;
3use std::sync::{
4 Arc,
5 atomic::{AtomicUsize, Ordering},
6};
7use std::time::Duration;
8use tokio::sync::RwLock;
9use tokio::task::{yield_now, JoinHandle};
10use tokio::{spawn, time::sleep};
11
12#[derive(Clone)]
13pub struct Entry<T>
14where
15 T: Send + Sync + Clone + 'static,
16{
17 pub value: T,
18}
19
20pub struct Inner<T>
21where
22 T: Send + Sync + Clone + 'static,
23{
24 pub entries: RwLock<Vec<Entry<T>>>,
25 pub cursor: AtomicUsize,
26}
27
28#[derive(Clone)]
30pub struct RoundRobin<T>
31where
32 T: Send + Sync + Clone + 'static,
33{
34 inner: Arc<Inner<T>>,
35}
36
37impl<T> RoundRobin<T>
38where
39 T: Send + Sync + Clone + 'static,
40{
41 pub fn new(entries: Vec<T>) -> Self {
43 Self {
44 inner: Arc::new(Inner {
45 entries: RwLock::new(entries.into_iter().map(|v| Entry { value: v }).collect()),
46 cursor: AtomicUsize::new(0),
47 }),
48 }
49 }
50
51 pub async fn update<F, R, N>(&self, handler: F) -> anyhow::Result<N>
53 where
54 F: Fn(Arc<Inner<T>>) -> R,
55 R: Future<Output = anyhow::Result<N>>,
56 {
57 handler(self.inner.clone()).await
58 }
59
60 pub async fn update_timer<F, R>(
65 &self,
66 handler: F,
67 interval: Duration,
68 ) -> anyhow::Result<JoinHandle<()>>
69 where
70 F: Fn(Arc<Inner<T>>) -> R + Send + Sync + 'static,
71 R: Future<Output = anyhow::Result<()>> + Send,
72 {
73 handler(self.inner.clone()).await?;
74
75 let inner = self.inner.clone();
76
77 Ok(spawn(async move {
78 loop {
79 sleep(interval).await;
80 let _ = handler(inner.clone()).await;
81 }
82 }))
83 }
84}
85
86impl<T> LoadBalancer<T> for RoundRobin<T>
87where
88 T: Send + Sync + Clone + 'static,
89{
90 async fn alloc(&self) -> T {
92 loop {
93 match LoadBalancer::try_alloc(self) {
94 Some(v) => return v,
95 None => yield_now().await,
96 }
97 }
98 }
99
100 fn try_alloc(&self) -> Option<T> {
102 let entries = self.inner.entries.try_read().ok()?;
103
104 if entries.is_empty() {
105 return None;
106 }
107
108 Some(
109 entries[self.inner.cursor.fetch_add(1, Ordering::Relaxed) % entries.len()]
110 .value
111 .clone(),
112 )
113 }
114}