pub mod scaling_bench;
use std::collections::HashMap;
use tokio::sync::mpsc;
#[derive(Debug, Clone)]
pub enum SimMessage {
Gossip {
from: u64,
data: Vec<u8>,
},
Ping(u64),
Pong(u64),
}
pub struct SimNode {
pub id: u64,
pub inbox: mpsc::Receiver<SimMessage>,
pub outboxes: HashMap<u64, mpsc::Sender<SimMessage>>,
}
pub struct SimCluster {
nodes: Vec<SimNode>,
senders: HashMap<u64, mpsc::Sender<SimMessage>>,
}
impl SimCluster {
const MAILBOX_CAPACITY: usize = 256;
pub fn new(n: usize) -> Self {
let mut senders: HashMap<u64, mpsc::Sender<SimMessage>> = HashMap::with_capacity(n);
let mut receivers: HashMap<u64, mpsc::Receiver<SimMessage>> = HashMap::with_capacity(n);
for i in 0..n as u64 {
let (tx, rx) = mpsc::channel::<SimMessage>(Self::MAILBOX_CAPACITY);
senders.insert(i, tx);
receivers.insert(i, rx);
}
let nodes: Vec<SimNode> = (0..n as u64)
.map(|i| {
let outboxes = senders.clone();
let inbox = receivers
.remove(&i)
.unwrap_or_else(|| unreachable!("receiver for node {i} was just inserted"));
SimNode {
id: i,
inbox,
outboxes,
}
})
.collect();
SimCluster { nodes, senders }
}
pub fn size(&self) -> usize {
self.nodes.len()
}
pub async fn gossip(&self, from: u64, to: u64, data: Vec<u8>) -> Result<(), String> {
let sender = self
.senders
.get(&to)
.ok_or_else(|| format!("no node with id {to}"))?;
sender
.send(SimMessage::Gossip { from, data })
.await
.map_err(|e| e.to_string())
}
pub async fn ping(&self, from: u64, to: u64) -> Result<(), String> {
let sender = self
.senders
.get(&to)
.ok_or_else(|| format!("no node with id {to}"))?;
sender
.send(SimMessage::Ping(from))
.await
.map_err(|e| e.to_string())
}
pub fn nodes(&self) -> &[SimNode] {
&self.nodes
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_sim_cluster_empty() {
let c = SimCluster::new(0);
assert_eq!(c.size(), 0);
}
#[test]
fn test_sim_cluster_single_node() {
let c = SimCluster::new(1);
assert_eq!(c.size(), 1);
}
#[test]
fn test_sim_cluster_10_nodes() {
let c = SimCluster::new(10);
assert_eq!(c.size(), 10);
}
#[test]
fn test_sim_cluster_1000_nodes() {
let c = SimCluster::new(1000);
assert_eq!(c.size(), 1000);
}
#[tokio::test]
async fn test_gossip_delivery() {
let cluster = SimCluster::new(3);
let result = cluster.gossip(0, 1, b"hello".to_vec()).await;
assert!(result.is_ok(), "gossip delivery should succeed");
}
#[tokio::test]
async fn test_gossip_unknown_target() {
let cluster = SimCluster::new(3);
let result = cluster.gossip(0, 999, b"hello".to_vec()).await;
assert!(result.is_err(), "gossip to unknown node should fail");
}
#[tokio::test]
async fn test_ping_delivery() {
let cluster = SimCluster::new(2);
let result = cluster.ping(0, 1).await;
assert!(result.is_ok());
}
}