use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::Arc;
use std::future::Future;
use tokio::sync::{mpsc, oneshot};
use crate::{ActorRef, ActorRefTrait, ActorTrait, Handler, SSSD};
#[derive(Debug)]
pub struct LoadBalancer<Actor, Message, State, Response, Error> {
name: String,
actor_refs: Arc<Vec<Arc<ActorRef<Actor, Message, State, Response, Error>>>>,
tx: mpsc::Sender<(Message, Option<oneshot::Sender<Result<Response, Error>>>)>,
running: AtomicBool,
turn: AtomicUsize,
}
impl<
Actor: Handler<
Actor = Actor,
State = State,
Response = Response,
Message = Message,
Error = Error,
> + SSSD,
Message: SSSD,
State: SSSD,
Response: SSSD,
Error: SSSD + std::error::Error + From<std::io::Error>,
> LoadBalancer<Actor, Message, State, Response, Error>
{
pub async fn new<F>(
name: impl Into<String>,
instances_amount: usize,
builder: F,
) -> Result<Arc<Self>, Error>
where
F: Fn(
usize,
) -> std::pin::Pin<
Box<
dyn Future<
Output = Result<Arc<ActorRef<Actor, Message, State, Response, Error>>, Error>,
>,
>,
>,
{
let mut instances = vec![];
for id in 0..instances_amount {
instances.push(builder(id).await?);
}
let (tx, mut rx) = mpsc::channel(1000);
let lb = Arc::new(Self {
name: name.into(),
actor_refs: Arc::new(instances),
tx: tx,
running: AtomicBool::new(false),
turn: AtomicUsize::new(0),
});
let lb_clone = lb.clone();
let handle = tokio::runtime::Handle::current();
{
let _ = handle.spawn(async move {
let lb = lb_clone.clone();
lb.running.store(true, Ordering::SeqCst);
loop {
tokio::select! {
_ = tokio::time::sleep(tokio::time::Duration::from_secs(1)) => {
if lb.running.load(Ordering::SeqCst) == false {
break;
}
},
recieved = rx.recv() => {
if let Some((msg, sender)) = recieved {
let actors = lb.actor_refs.clone();
let turn = lb
.turn
.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| {
Some((x + 1) % actors.len())
});
log::debug!("<{}> Got message: {:?}, Turn: {:?}", lb.name, msg, turn);
let turn = match turn {
Err(val) => {
log::error!("Failed to update LoadBalancer.turn");
val
}
Ok(val) => val,
};
let msg_dbg = format!("{:?}", msg);
if let Some(sender) = sender {
let msg_back = actors[turn].ask(msg).await;
let result = sender.send(msg_back);
if result.is_err() {
log::error!(
"<{}> Error: {:?} on message: {}",
lb.name,
result,
msg_dbg
);
}
} else {
let result = actors[turn].send(msg).await;
log::debug!(
"<{}> Actor state: {:?}",
lb.name,
actors[turn].state().await
);
if result.is_err() {
log::error!(
"<{}> Error: {:?} on message: {}",
lb.name,
result,
msg_dbg
);
}
}
}
}
}
}
});
}
Ok(lb)
}
pub async fn send(&self, msg: Message) -> Result<(), std::io::Error> {
log::debug!("<{}> Push message: {:?}", self.name, msg);
let r = self.tx.send((msg, None)).await;
if r.is_err() {
return Err(std::io::Error::new(std::io::ErrorKind::Other, "Err").into());
}
Ok(())
}
pub async fn ask(&self, msg: Message) -> Result<Response, Error> {
log::debug!("<{}> Push message: {:?}", self.name, msg);
let (sender, reciever) = oneshot::channel();
{
let r = self.tx.send((msg, Some(sender))).await;
if r.is_err() {
return Err(std::io::Error::new(std::io::ErrorKind::Other, "Err").into());
}
}
let response = reciever.await;
match response {
Ok(r) => r,
Err(_) => {
return Err(std::io::Error::new(std::io::ErrorKind::Other, "Err").into());
}
}
}
pub async fn stop(&self) -> Result<(), Error> {
if self.running.load(Ordering::SeqCst) == false {
return Ok(());
}
for actor_ref in &*self.actor_refs {
actor_ref.stop().await?;
}
Ok(())
}
pub async fn state(
&self,
id: usize,
) -> Result<Arc<futures::lock::Mutex<State>>, std::io::Error> {
self.actor_refs[id].state().await
}
}