use std::collections::HashMap;
use std::fmt::Debug;
use std::sync::{Arc as StdArc, atomic::AtomicUsize};
use serde::{Serialize, de::DeserializeOwned};
use tokio::sync::{Notify, mpsc};
use tokio::task::JoinSet;
use tokio::time::{Duration, sleep};
use crate::messages::client_broker_message::ClientBrokerMessage;
use crate::network::client::Client;
use crate::network::network_topology::{Connection, NetworkTopology};
use crate::utilities::tx_sender::add_to_tx_with_retry;
pub struct ClientBroker<T> {
messaging_active: bool,
client_senders: HashMap<String, mpsc::Sender<T>>,
client_threads: JoinSet<()>,
}
impl<T> ClientBroker<T>
where
T: Debug + Send + 'static + DeserializeOwned + Sync + Clone + Serialize,
{
pub fn init(
network_topology: StdArc<NetworkTopology>,
container_state: StdArc<AtomicUsize>,
container_state_notify: StdArc<Notify>,
) -> Self {
let connections: Vec<Connection> = network_topology
.client_connections
.client_connection_vec
.clone();
let mut client_senders = HashMap::new();
let mut client_threads = JoinSet::new();
for conn in connections {
let (client_tx, client_rx) = mpsc::channel::<T>(32);
client_senders.insert(conn.name.clone(), client_tx);
let container_state_clone = container_state.clone();
let container_state_notify_clone = container_state_notify.clone();
let client = Client::new(
conn.name.clone(),
conn.address.clone(),
client_rx,
container_state_clone,
container_state_notify_clone,
5,
);
client_threads.spawn(async move {
if let Err(e) = client.run().await {
log::error!("Client {} encountered an error: {e:?}", conn.name);
}
});
}
Self {
messaging_active: true,
client_senders,
client_threads,
}
}
pub async fn handle_message(&self, message: ClientBrokerMessage<T>) {
if self.messaging_active {
for target_client in &message.target_clients {
if let Some(sender) = self.client_senders.get(target_client) {
add_to_tx_with_retry(sender, &message.message, "ClientBroker", target_client)
.await;
} else {
log::error!("Unknown TargetClient: {target_client}");
}
}
} else {
log::trace!(
"Message to {} blocked by inactive ClientBroker",
message.target_clients.concat()
);
}
}
pub fn clone_all_senders(&self) -> HashMap<String, mpsc::Sender<T>> {
self.client_senders.clone()
}
pub async fn forward_shutdown(&mut self, message: T) {
for (name, sender) in &self.client_senders {
if let Err(e) = sender.send(message.clone()).await {
log::error!("Failed to send shutdown message to {name}: {e:?}");
}
}
sleep(Duration::from_millis(500)).await;
self.messaging_active = false;
}
pub async fn shutdown(&mut self) {
while self.client_threads.join_next().await.is_some() {}
self.client_senders.clear(); log::info!("ClientBroker has closed all Clients");
}
}
impl<T> Drop for ClientBroker<T> {
fn drop(&mut self) {
log::info!("ClientBroker has been closed");
}
}