use crate::ZmqError;
use crate::socket::connection_iface::ISocketConnection;
use parking_lot::RwLock;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use tokio::sync::Notify;
#[derive(Clone)]
pub(crate) struct Peer {
pub uri: String,
pub iface: Arc<dyn ISocketConnection>,
}
pub(crate) struct LoadBalancer {
peers: RwLock<Vec<Arc<Peer>>>,
next_idx: AtomicUsize,
notify_waiters: Arc<Notify>,
deactivated: AtomicBool,
}
impl std::fmt::Debug for LoadBalancer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("LoadBalancer")
.field("peer_count", &self.peers.read().len())
.finish()
}
}
impl Default for LoadBalancer {
fn default() -> Self {
Self {
peers: RwLock::new(Vec::new()),
next_idx: AtomicUsize::new(0),
notify_waiters: Arc::new(Notify::new()),
deactivated: AtomicBool::new(false),
}
}
}
impl LoadBalancer {
pub fn new() -> Self {
Self::default()
}
pub fn add_connection(&self, endpoint_uri: String, iface: Arc<dyn ISocketConnection>) {
let mut peers = self.peers.write();
if !peers.iter().any(|p| p.uri == endpoint_uri) {
peers.push(Arc::new(Peer {
uri: endpoint_uri.clone(),
iface,
}));
tracing::trace!(uri = %endpoint_uri, "LoadBalancer added connection");
self.notify_waiters.notify_waiters();
} else {
tracing::trace!(uri = %endpoint_uri, "LoadBalancer: Connection already present, not adding again.");
}
}
pub fn remove_connection(&self, endpoint_uri: &str) {
let mut peers = self.peers.write();
if let Some(pos) = peers.iter().position(|p| p.uri == endpoint_uri) {
peers.remove(pos);
tracing::trace!(uri = %endpoint_uri, "LoadBalancer removed connection");
} else {
tracing::trace!(uri = %endpoint_uri, "LoadBalancer: Connection not found for removal.");
}
}
pub fn get_next_connection(&self) -> Option<Arc<Peer>> {
let peers = self.peers.read();
let len = peers.len();
if len == 0 {
return None;
}
let idx = self.next_idx.fetch_add(1, Ordering::Relaxed) % len;
Some(Arc::clone(&peers[idx]))
}
pub async fn wait_for_connection(&self) -> Result<(), ZmqError> {
let notify = self.notify_waiters.clone();
loop {
if self.deactivated.load(Ordering::Acquire) {
return Err(ZmqError::InvalidState("Socket closed".into()));
}
if !self.peers.read().is_empty() {
return Ok(());
}
notify.notified().await;
}
}
pub fn has_connections(&self) -> bool {
!self.peers.read().is_empty()
}
pub fn connection_count(&self) -> usize {
self.peers.read().len()
}
pub fn deactivate(&self) {
self.deactivated.store(true, Ordering::Release);
self.notify_waiters.notify_waiters();
}
}