use std::{
marker::PhantomData,
sync::{Arc, Mutex},
vec,
};
use ruda_communication::{CommunicationChannel, Message, Protocol, ProtocolClient};
use serde::{Deserialize, Serialize};
use tokio::sync::{Notify, RwLock};
use crate::{NodeId, node::base::NodeState};
pub(crate) struct SyncService<P: Protocol> {
node_state: Arc<RwLock<Option<NodeState>>>,
syncing_peers: Mutex<Vec<NodeId>>,
sync_notif: Notify,
_p: PhantomData<P>,
}
#[derive(Debug, Serialize, Deserialize)]
struct SyncRequest(NodeId);
impl<P: Protocol> SyncService<P> {
pub fn new(node_state: Arc<RwLock<Option<NodeState>>>) -> Self {
Self {
node_state,
syncing_peers: Mutex::new(vec![]),
sync_notif: Notify::new(),
_p: PhantomData,
}
}
fn add_syncing_peer(&self, peer: NodeId) {
let mut syncing_peers = self.syncing_peers.lock().unwrap();
syncing_peers.push(peer);
}
pub async fn sync(&self) {
let node_state = self.node_state.read().await;
let node_state = node_state
.as_ref()
.expect("Trying to sync a node before having registered to the orchestrator");
self.add_syncing_peer(node_state.node_id);
for (id, addr) in &node_state.nodes {
if *id == node_state.node_id {
continue;
}
let mut connection = P::Client::connect(addr.clone(), "sync")
.await
.expect("Couldn't connect to peer for sync");
let msg = SyncRequest(node_state.node_id);
let sync_bytes = rmp_serde::to_vec(&msg).unwrap();
connection
.send(Message::new(sync_bytes.into()))
.await
.expect("Peer closed connection unexpectedly");
}
loop {
{
let mut syncing_peers = self.syncing_peers.lock().unwrap().to_vec();
syncing_peers.sort();
let mut all_node_ids = node_state.nodes.keys().cloned().collect::<Vec<_>>();
all_node_ids.sort();
if syncing_peers == all_node_ids {
syncing_peers.clear();
return;
}
}
self.sync_notif.notified().await
}
}
pub async fn handle_sync_connection<C: CommunicationChannel>(&self, mut channel: C) {
let msg = channel.recv().await.unwrap();
let Some(msg) = msg else {
return;
};
let msg = rmp_serde::from_slice::<SyncRequest>(&msg.data).unwrap();
self.add_syncing_peer(msg.0);
self.sync_notif.notify_waiters();
}
}