153 lines
4.4 KiB
Rust
153 lines
4.4 KiB
Rust
use std::{
|
|
hash::{Hash, Hasher},
|
|
sync::Arc,
|
|
};
|
|
|
|
use dashmap::DashMap;
|
|
use once_cell::sync::Lazy;
|
|
use tokio::{sync::RwLock, task::JoinSet};
|
|
|
|
use crate::common::PeerId;
|
|
|
|
use super::{
|
|
service::{GetGlobalPeerMapResponse, GlobalPeerMap, PeerCenterService, PeerInfoForGlobalMap},
|
|
Digest, Error,
|
|
};
|
|
|
|
pub(crate) struct PeerCenterServerGlobalData {
|
|
pub global_peer_map: GlobalPeerMap,
|
|
pub digest: Digest,
|
|
pub update_time: std::time::Instant,
|
|
pub peer_update_time: DashMap<PeerId, std::time::Instant>,
|
|
}
|
|
|
|
impl PeerCenterServerGlobalData {
|
|
fn new() -> Self {
|
|
PeerCenterServerGlobalData {
|
|
global_peer_map: GlobalPeerMap::new(),
|
|
digest: Digest::default(),
|
|
update_time: std::time::Instant::now(),
|
|
peer_update_time: DashMap::new(),
|
|
}
|
|
}
|
|
}
|
|
|
|
// a global unique instance for PeerCenterServer
|
|
pub(crate) static GLOBAL_DATA: Lazy<DashMap<PeerId, Arc<RwLock<PeerCenterServerGlobalData>>>> =
|
|
Lazy::new(DashMap::new);
|
|
|
|
pub(crate) fn get_global_data(node_id: PeerId) -> Arc<RwLock<PeerCenterServerGlobalData>> {
|
|
GLOBAL_DATA
|
|
.entry(node_id)
|
|
.or_insert_with(|| Arc::new(RwLock::new(PeerCenterServerGlobalData::new())))
|
|
.value()
|
|
.clone()
|
|
}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct PeerCenterServer {
|
|
// every peer has its own server, so use per-struct dash map is ok.
|
|
my_node_id: PeerId,
|
|
digest_map: DashMap<PeerId, Digest>,
|
|
|
|
tasks: Arc<JoinSet<()>>,
|
|
}
|
|
|
|
impl PeerCenterServer {
|
|
pub fn new(my_node_id: PeerId) -> Self {
|
|
let mut tasks = JoinSet::new();
|
|
tasks.spawn(async move {
|
|
loop {
|
|
tokio::time::sleep(std::time::Duration::from_secs(10)).await;
|
|
PeerCenterServer::clean_outdated_peer(my_node_id).await;
|
|
}
|
|
});
|
|
|
|
PeerCenterServer {
|
|
my_node_id,
|
|
digest_map: DashMap::new(),
|
|
|
|
tasks: Arc::new(tasks),
|
|
}
|
|
}
|
|
|
|
async fn clean_outdated_peer(my_node_id: PeerId) {
|
|
let data = get_global_data(my_node_id);
|
|
let mut locked_data = data.write().await;
|
|
let now = std::time::Instant::now();
|
|
let mut to_remove = Vec::new();
|
|
for kv in locked_data.peer_update_time.iter() {
|
|
if now.duration_since(*kv.value()).as_secs() > 20 {
|
|
to_remove.push(*kv.key());
|
|
}
|
|
}
|
|
for peer_id in to_remove {
|
|
locked_data.global_peer_map.map.remove(&peer_id);
|
|
locked_data.peer_update_time.remove(&peer_id);
|
|
}
|
|
}
|
|
}
|
|
|
|
#[tarpc::server]
|
|
impl PeerCenterService for PeerCenterServer {
|
|
#[tracing::instrument()]
|
|
async fn report_peers(
|
|
self,
|
|
_: tarpc::context::Context,
|
|
my_peer_id: PeerId,
|
|
peers: Option<PeerInfoForGlobalMap>,
|
|
digest: Digest,
|
|
) -> Result<(), Error> {
|
|
tracing::trace!("receive report_peers");
|
|
|
|
let data = get_global_data(self.my_node_id);
|
|
let mut locked_data = data.write().await;
|
|
locked_data
|
|
.peer_update_time
|
|
.insert(my_peer_id, std::time::Instant::now());
|
|
|
|
let old_digest = self.digest_map.get(&my_peer_id);
|
|
// if digest match, no need to update
|
|
if let Some(old_digest) = old_digest {
|
|
if *old_digest == digest {
|
|
return Ok(());
|
|
}
|
|
}
|
|
|
|
if peers.is_none() {
|
|
return Err(Error::DigestMismatch);
|
|
}
|
|
|
|
self.digest_map.insert(my_peer_id, digest);
|
|
locked_data
|
|
.global_peer_map
|
|
.map
|
|
.insert(my_peer_id, peers.unwrap());
|
|
|
|
let mut hasher = std::collections::hash_map::DefaultHasher::new();
|
|
locked_data.global_peer_map.map.hash(&mut hasher);
|
|
locked_data.digest = hasher.finish() as Digest;
|
|
locked_data.update_time = std::time::Instant::now();
|
|
|
|
Ok(())
|
|
}
|
|
|
|
async fn get_global_peer_map(
|
|
self,
|
|
_: tarpc::context::Context,
|
|
digest: Digest,
|
|
) -> Result<Option<GetGlobalPeerMapResponse>, Error> {
|
|
let data = get_global_data(self.my_node_id);
|
|
if digest == data.read().await.digest {
|
|
return Ok(None);
|
|
}
|
|
|
|
let data = get_global_data(self.my_node_id);
|
|
let locked_data = data.read().await;
|
|
Ok(Some(GetGlobalPeerMapResponse {
|
|
global_peer_map: locked_data.global_peer_map.clone(),
|
|
digest: locked_data.digest,
|
|
}))
|
|
}
|
|
}
|