1use crate::{BoxLoadBalancer, LoadBalancer};
2use async_trait::async_trait;
3use std::future::Future;
4use std::sync::{
5 Arc,
6 atomic::{AtomicUsize, Ordering},
7};
8use tokio::sync::RwLock;
9
10#[derive(Clone)]
12pub struct Entry<T>
13where
14 T: Send + Sync + Clone + 'static,
15{
16 pub value: T,
18}
19
20pub struct SimpleLoadBalancerRef<T>
22where
23 T: Send + Sync + Clone + 'static,
24{
25 pub entries: RwLock<Vec<Entry<T>>>,
27 pub cursor: AtomicUsize,
29}
30
31#[derive(Clone)]
33pub struct SimpleLoadBalancer<T>
34where
35 T: Send + Sync + Clone + 'static,
36{
37 inner: Arc<SimpleLoadBalancerRef<T>>,
39}
40
41impl<T> SimpleLoadBalancer<T>
42where
43 T: Send + Sync + Clone + 'static,
44{
45 pub fn new(entries: Vec<T>) -> Self {
47 Self {
48 inner: Arc::new(SimpleLoadBalancerRef {
49 entries: RwLock::new(entries.into_iter().map(|v| Entry { value: v }).collect()),
50 cursor: AtomicUsize::new(0),
51 }),
52 }
53 }
54
55 pub async fn update<F, R>(&self, handle: F) -> anyhow::Result<()>
57 where
58 F: Fn(Arc<SimpleLoadBalancerRef<T>>) -> R,
59 R: Future<Output = anyhow::Result<()>>,
60 {
61 handle(self.inner.clone()).await
62 }
63}
64
65impl<T> LoadBalancer<T> for SimpleLoadBalancer<T>
66where
67 T: Send + Sync + Clone + 'static,
68{
69 async fn alloc(&self) -> Option<T> {
71 let entries = self.inner.entries.read().await;
72
73 if entries.is_empty() {
74 return None;
75 }
76
77 Some(
78 entries[self.inner.cursor.fetch_add(1, Ordering::Relaxed) % entries.len()]
79 .value
80 .clone(),
81 )
82 }
83
84 fn try_alloc(&self) -> Option<T> {
86 let entries = self.inner.entries.try_read().ok()?;
87
88 if entries.is_empty() {
89 return None;
90 }
91
92 Some(
93 entries[self.inner.cursor.fetch_add(1, Ordering::Relaxed) % entries.len()]
94 .value
95 .clone(),
96 )
97 }
98}
99
100#[async_trait]
101impl<T> BoxLoadBalancer<T> for SimpleLoadBalancer<T>
102where
103 T: Send + Sync + Clone + 'static,
104{
105 async fn alloc(&self) -> Option<T> {
107 LoadBalancer::alloc(self).await
108 }
109
110 fn try_alloc(&self) -> Option<T> {
112 LoadBalancer::try_alloc(self)
113 }
114}