use std::net::SocketAddr;
use std::sync::Arc;
use std::sync::atomic::AtomicU32;
use async_trait::async_trait;
use bytes::Bytes;
use dashmap::DashMap;
use tokio::io::AsyncWriteExt;
use tokio::net::TcpStream;
use tokio::sync::{Mutex, mpsc, oneshot};
use crate::broker::broker::{Broker, PublishOutcome};
use crate::broker::fanout::{ConnectionSink, SubscribeIntent, SubscriptionId};
use crate::broker::wire::WireMsg;
use crate::error::{Result, RiftError};
pub struct RemoteBroker {
writer: Mutex<tokio::io::WriteHalf<TcpStream>>,
pending: Arc<DashMap<u32, oneshot::Sender<WireMsg>>>,
next_id: AtomicU32,
sinks: Arc<DashMap<u64, mpsc::Sender<Bytes>>>,
_reader_handle: tokio::task::JoinHandle<()>,
}
impl RemoteBroker {
pub async fn connect(addr: SocketAddr) -> Result<Self> {
let stream = TcpStream::connect(addr)
.await
.map_err(|e| RiftError::System(crate::error::SystemReject::Internal(e.to_string())))?;
let (reader, writer) = tokio::io::split(stream);
let pending: Arc<DashMap<u32, oneshot::Sender<WireMsg>>> = Arc::new(DashMap::new());
let sinks: Arc<DashMap<u64, mpsc::Sender<Bytes>>> = Arc::new(DashMap::new());
let _pending_reader = pending.clone();
let _sinks_reader = sinks.clone();
let reader_handle = tokio::spawn(async move {
let mut buf = vec![0u8; 4096];
let mut read_half = reader;
loop {
match tokio::io::AsyncReadExt::read(&mut read_half, &mut buf).await {
Ok(0) => break, Ok(_) => {
}
Err(_) => break,
}
}
drop(read_half);
});
Ok(Self {
writer: Mutex::new(writer),
pending,
next_id: AtomicU32::new(1),
sinks,
_reader_handle: reader_handle,
})
}
async fn request(&self, msg: WireMsg) -> Result<WireMsg> {
let id = self
.next_id
.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
let (tx, rx) = oneshot::channel();
self.pending.insert(id, tx);
let mut payload = Vec::new();
ciborium::into_writer(&msg, &mut payload).map_err(|e| {
RiftError::Frame(crate::error::FrameReject::FrameInvalid(e.to_string()))
})?;
let len = (payload.len() as u32).to_be_bytes();
let mut writer = self.writer.lock().await;
writer
.write_all(&len)
.await
.map_err(|e| RiftError::System(crate::error::SystemReject::Internal(e.to_string())))?;
writer
.write_all(&payload)
.await
.map_err(|e| RiftError::System(crate::error::SystemReject::Internal(e.to_string())))?;
match tokio::time::timeout(std::time::Duration::from_secs(30), rx).await {
Ok(Ok(resp)) => Ok(resp),
Ok(Err(_)) => Err(RiftError::System(crate::error::SystemReject::Internal(
"broker connection closed".into(),
))),
Err(_) => Err(RiftError::System(crate::error::SystemReject::Internal(
"broker request timed out".into(),
))),
}
}
}
#[async_trait]
impl Broker for RemoteBroker {
async fn publish(&self, frame: &crate::frame::Frame) -> Result<PublishOutcome> {
let resp = self
.request(WireMsg::Publish {
frame: frame.clone(),
})
.await?;
match resp {
WireMsg::PublishResult { outcome } => Ok(outcome),
WireMsg::Error { code, message } => Err(RiftError::System(
crate::error::SystemReject::Internal(format!("broker error: {code} — {message}")),
)),
_ => Err(RiftError::System(crate::error::SystemReject::Internal(
"unexpected broker response".into(),
))),
}
}
async fn subscribe(
&self,
topic: &str,
intent: SubscribeIntent,
sink: ConnectionSink,
) -> Result<SubscriptionId> {
let raw_id = crate::broker::fanout::new_sink_id();
let (tx, mut rx) = mpsc::channel::<Bytes>(256);
self.sinks.insert(raw_id, tx);
let sink_clone = sink.clone();
tokio::spawn(async move {
while let Some(payload) = rx.recv().await {
let _ = sink_clone.deliver(payload);
}
});
let resp = self
.request(WireMsg::Subscribe {
topic: topic.to_string(),
intent,
sink_id: raw_id,
})
.await?;
match resp {
WireMsg::SubscribeResult { id } => Ok(SubscriptionId(id)),
WireMsg::Error { code, message } => Err(RiftError::System(
crate::error::SystemReject::Internal(format!("broker error: {code} — {message}")),
)),
_ => Err(RiftError::System(crate::error::SystemReject::Internal(
"unexpected broker response".into(),
))),
}
}
async fn unsubscribe(&self, _id: SubscriptionId) -> Result<bool> {
Ok(true)
}
async fn drop_sink(&self, sink_id: u64) -> usize {
self.sinks.remove(&sink_id);
let _ = self.request(WireMsg::DropSink { sink_id }).await;
1
}
async fn replay(&self, topic: &str, from: i64, to: i64) -> Result<Vec<Bytes>> {
let resp = self
.request(WireMsg::Replay {
topic: topic.to_string(),
from,
to,
})
.await?;
match resp {
WireMsg::ReplayResult { entries } => Ok(entries),
WireMsg::Error { code, message } => Err(RiftError::System(
crate::error::SystemReject::Internal(format!("broker error: {code} — {message}")),
)),
_ => Err(RiftError::System(crate::error::SystemReject::Internal(
"unexpected broker response".into(),
))),
}
}
async fn snapshot(&self, topic: &str) -> Result<Option<crate::storage::StoredSnapshot>> {
let resp = self
.request(WireMsg::Snapshot {
topic: topic.to_string(),
})
.await?;
match resp {
WireMsg::SnapshotResult { snapshot } => Ok(snapshot),
_ => Err(RiftError::System(crate::error::SystemReject::Internal(
"unexpected broker response".into(),
))),
}
}
async fn subscriber_count(&self, topic: &str) -> usize {
let resp = self
.request(WireMsg::SubscriberCount {
topic: topic.to_string(),
})
.await;
match resp {
Ok(WireMsg::SubscriberCountResult { count }) => count,
_ => 0,
}
}
async fn head_offset(&self, topic: &str) -> i64 {
let resp = self
.request(WireMsg::HeadOffset {
topic: topic.to_string(),
})
.await;
match resp {
Ok(WireMsg::HeadOffsetResult { offset }) => offset,
_ => 0,
}
}
}