use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, RwLock};
use serde::Serialize;
use serde::de::DeserializeOwned;
use crate::actor::{Actor, Handler, RemoteMessage};
use crate::endpoint::Endpoint;
use crate::listing::{ListingEvent, ReceptionKey, WatchedListing};
use crate::receptionist::Receptionist;
use crate::wire::SendError;
#[derive(Debug, Clone, Default)]
pub enum RoutingStrategy {
#[default]
RoundRobin,
Random,
Broadcast,
}
pub struct Router<A: Actor> {
endpoints: Vec<Endpoint<A>>,
strategy: RoutingStrategy,
counter: AtomicUsize,
}
impl<A: Actor + 'static> Router<A> {
pub fn new(endpoints: Vec<Endpoint<A>>, strategy: RoutingStrategy) -> Self {
Self {
endpoints,
strategy,
counter: AtomicUsize::new(0),
}
}
pub async fn send<M>(&self, msg: M) -> Result<M::Result, SendError>
where
A: Handler<M>,
M: RemoteMessage + Clone,
M::Result: Serialize + DeserializeOwned,
{
if self.endpoints.is_empty() {
return Err(SendError::MailboxClosed);
}
match self.strategy {
RoutingStrategy::RoundRobin => {
let idx = self.counter.fetch_add(1, Ordering::Relaxed) % self.endpoints.len();
self.endpoints[idx].send(msg).await
}
RoutingStrategy::Random => {
use rand::Rng;
let idx = rand::rng().random_range(0..self.endpoints.len());
self.endpoints[idx].send(msg).await
}
RoutingStrategy::Broadcast => {
self.endpoints[0].send(msg).await
}
}
}
pub async fn broadcast<M>(&self, msg: M) -> Vec<Result<M::Result, SendError>>
where
A: Handler<M>,
M: RemoteMessage + Clone,
M::Result: Serialize + DeserializeOwned,
{
let mut results = Vec::with_capacity(self.endpoints.len());
for ep in &self.endpoints {
results.push(ep.send(msg.clone()).await);
}
results
}
pub async fn send_quorum<M>(&self, msg: M, k: usize) -> Vec<Result<M::Result, SendError>>
where
A: Handler<M>,
M: RemoteMessage + Clone,
M::Result: Serialize + DeserializeOwned,
{
if self.endpoints.is_empty() {
return vec![Err(SendError::MailboxClosed)];
}
let mut join_set = tokio::task::JoinSet::new();
for ep in &self.endpoints {
let ep = ep.clone();
let msg = msg.clone();
join_set.spawn(async move { ep.send(msg).await });
}
let target = k.min(self.endpoints.len());
let mut results = Vec::with_capacity(target);
let mut successes = 0;
while let Some(join_result) = join_set.join_next().await {
let result = join_result.unwrap_or(Err(SendError::ResponseDropped));
if result.is_ok() {
successes += 1;
}
results.push(result);
if successes >= target {
join_set.abort_all();
break;
}
}
results
}
pub async fn scatter_gather<M>(
&self,
msg: M,
timeout: std::time::Duration,
) -> Vec<Result<M::Result, SendError>>
where
A: Handler<M>,
M: RemoteMessage + Clone,
M::Result: Serialize + DeserializeOwned,
{
if self.endpoints.is_empty() {
return vec![Err(SendError::MailboxClosed)];
}
let mut join_set = tokio::task::JoinSet::new();
for ep in &self.endpoints {
let ep = ep.clone();
let msg = msg.clone();
join_set.spawn(async move { ep.send(msg).await });
}
let mut results = Vec::with_capacity(self.endpoints.len());
let deadline = tokio::time::Instant::now() + timeout;
loop {
tokio::select! {
Some(join_result) = join_set.join_next() => {
results.push(join_result.unwrap_or(Err(SendError::ResponseDropped)));
if results.len() == self.endpoints.len() {
break;
}
}
_ = tokio::time::sleep_until(deadline) => {
join_set.abort_all();
break;
}
}
}
results
}
pub async fn first_response<M>(&self, msg: M) -> Result<M::Result, SendError>
where
A: Handler<M>,
M: RemoteMessage + Clone,
M::Result: Serialize + DeserializeOwned,
{
if self.endpoints.is_empty() {
return Err(SendError::MailboxClosed);
}
let mut join_set = tokio::task::JoinSet::new();
for ep in &self.endpoints {
let ep = ep.clone();
let msg = msg.clone();
join_set.spawn(async move { ep.send(msg).await });
}
let mut last_error = SendError::MailboxClosed;
while let Some(join_result) = join_set.join_next().await {
match join_result.unwrap_or(Err(SendError::ResponseDropped)) {
Ok(result) => {
join_set.abort_all();
return Ok(result);
}
Err(e) => last_error = e,
}
}
Err(last_error)
}
pub fn fan_out<M>(&self, msg: M)
where
A: Handler<M>,
M: RemoteMessage + Clone + 'static,
M::Result: Serialize + DeserializeOwned,
{
for ep in &self.endpoints {
let ep = ep.clone();
let msg = msg.clone();
tokio::spawn(async move {
let _ = ep.send(msg).await;
});
}
}
pub fn add(&mut self, endpoint: Endpoint<A>) {
self.endpoints.push(endpoint);
}
pub fn remove(&mut self, index: usize) {
if index < self.endpoints.len() {
self.endpoints.remove(index);
}
}
pub fn len(&self) -> usize {
self.endpoints.len()
}
pub fn is_empty(&self) -> bool {
self.endpoints.is_empty()
}
}
type Pool<A> = Arc<RwLock<Vec<(String, Endpoint<A>)>>>;
pub struct PoolRouter<A: Actor> {
pool: Pool<A>,
strategy: RoutingStrategy,
counter: AtomicUsize,
}
impl<A: Actor + 'static> PoolRouter<A> {
pub fn new(
receptionist: &Receptionist,
key: ReceptionKey<A>,
strategy: RoutingStrategy,
) -> Self {
let watched = receptionist.watched_listing(key);
Self::from_watched_listing(watched, strategy)
}
pub fn from_watched_listing(watched: WatchedListing<A>, strategy: RoutingStrategy) -> Self {
let pool: Pool<A> = Arc::new(RwLock::new(Vec::new()));
let pool_clone = Arc::clone(&pool);
tokio::spawn(Self::run_watcher(pool_clone, watched));
Self {
pool,
strategy,
counter: AtomicUsize::new(0),
}
}
async fn run_watcher(pool: Pool<A>, mut watched: WatchedListing<A>) {
while let Some(event) = watched.next().await {
match event {
ListingEvent::Added { label, endpoint } => {
let mut pool = pool.write().unwrap();
if !pool.iter().any(|(l, _)| l == &label) {
pool.push((label, endpoint));
}
}
ListingEvent::Removed { label } => {
let mut pool = pool.write().unwrap();
pool.retain(|(l, _)| l != &label);
}
}
}
}
pub async fn send<M>(&self, msg: M) -> Result<M::Result, SendError>
where
A: Handler<M>,
M: RemoteMessage + Clone,
M::Result: Serialize + DeserializeOwned,
{
let endpoint = {
let pool = self.pool.read().unwrap();
if pool.is_empty() {
return Err(SendError::MailboxClosed);
}
let idx = match self.strategy {
RoutingStrategy::RoundRobin => {
self.counter.fetch_add(1, Ordering::Relaxed) % pool.len()
}
RoutingStrategy::Random => {
use rand::Rng;
rand::rng().random_range(0..pool.len())
}
RoutingStrategy::Broadcast => 0,
};
pool[idx].1.clone()
};
endpoint.send(msg).await
}
pub async fn broadcast<M>(&self, msg: M) -> Vec<Result<M::Result, SendError>>
where
A: Handler<M>,
M: RemoteMessage + Clone,
M::Result: Serialize + DeserializeOwned,
{
let endpoints: Vec<_> = {
let pool = self.pool.read().unwrap();
pool.iter().map(|(_, ep)| ep.clone()).collect()
};
let mut join_set = tokio::task::JoinSet::new();
for ep in endpoints {
let msg = msg.clone();
join_set.spawn(async move { ep.send(msg).await });
}
let mut results = Vec::new();
while let Some(join_result) = join_set.join_next().await {
results.push(join_result.unwrap_or(Err(SendError::ResponseDropped)));
}
results
}
pub fn len(&self) -> usize {
self.pool.read().unwrap().len()
}
pub fn is_empty(&self) -> bool {
self.pool.read().unwrap().is_empty()
}
pub fn labels(&self) -> Vec<String> {
self.pool
.read()
.unwrap()
.iter()
.map(|(l, _)| l.clone())
.collect()
}
}