use std::net::SocketAddr;
use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
use std::time::{Duration, Instant};
use std::sync::{Arc, OnceLock};
use async_trait::async_trait;
use dashmap::DashMap;
use serde_json::{Value, json};
use tokio::sync::{Semaphore, mpsc};
use super::mesh_forward::{InferPeer, ServeGate, serve_infer};
use super::table::PlacementTable;
use super::types::{MeshDialer, ProxyAudit, ProxyError, ProxyLimits, qualifies};
use super::upstream::Upstream;
use crate::ipc::{KernelMessage, MessagePayload, MessageTarget};
use crate::mesh::{MeshError, MeshStream};
use crate::mesh_admit::{Grant, hex_decode, hex_encode};
use crate::mesh_delivery::PeerCtx;
use crate::mesh_framing::{FrameType, MeshFrame};
use crate::mesh_runtime::{INFER_TOPIC, MeshRuntime, PeerControlSink};
use crate::mesh_service_adv::ServiceAdvertisement;
const RESP_BUFFER: usize = 64;
const INTAKE: usize = 32;
const MAX_PENDING: usize = 256;
const REFUSALS: u32 = 8;
const REFUSAL_WINDOW: Duration = Duration::from_secs(10);
type Pending = Arc<DashMap<(String, u64), mpsc::Sender<Vec<u8>>>>;
pub struct InferHub {
rt: Arc<MeshRuntime>,
table: OnceLock<Arc<PlacementTable>>,
upstream: Arc<Upstream>,
gate: Arc<ServeGate>,
audit: Option<Arc<dyn ProxyAudit>>,
grants: DashMap<String, (u64, Grant)>,
max_pending: AtomicUsize,
refusals: DashMap<String, (Instant, u32)>,
pending: Pending,
next_id: AtomicU64,
intake: Arc<Semaphore>,
limits: ProxyLimits,
me: OnceLock<std::sync::Weak<Self>>,
}
fn msg(value: Value) -> KernelMessage {
KernelMessage::new(
0,
MessageTarget::Topic(INFER_TOPIC.to_string()),
MessagePayload::Json(value),
)
}
fn mesh_err(e: impl std::fmt::Display) -> MeshError {
MeshError::Transport(e.to_string())
}
fn split_frame(data: &[u8]) -> Result<MeshFrame, MeshError> {
if data.len() < 5 {
return Err(mesh_err("short frame"));
}
MeshFrame::decode(&data[4..])
}
impl InferHub {
pub fn new(
rt: Arc<MeshRuntime>,
upstream: Arc<Upstream>,
gate: Arc<ServeGate>,
audit: Option<Arc<dyn ProxyAudit>>,
) -> Arc<Self> {
let limits = upstream.limits().clone();
let hub = Arc::new(Self {
rt,
table: OnceLock::new(),
upstream,
gate,
audit,
grants: DashMap::new(),
max_pending: AtomicUsize::new(MAX_PENDING),
refusals: DashMap::new(),
pending: Arc::new(DashMap::new()),
next_id: AtomicU64::new(1),
intake: Arc::new(Semaphore::new(INTAKE)),
limits,
me: OnceLock::new(),
});
let _ = hub.me.set(Arc::downgrade(&hub));
hub
}
pub fn attach(&self, table: Arc<PlacementTable>) {
let _ = self.table.set(table);
}
pub fn install(self: &Arc<Self>) {
self.rt.set_control_sink(INFER_TOPIC, self.clone());
let hub = self.clone();
let mut rx = self.rt.subscribe_peer_events();
tokio::spawn(async move {
use tokio::sync::broadcast::error::RecvError;
loop {
match rx.recv().await {
Ok(crate::mesh_discovery::MeshPeerEvent::Left { node_id }) => {
hub.grants.remove(&node_id);
}
Ok(_) => {}
Err(RecvError::Lagged(_)) => hub.grants.clear(),
Err(RecvError::Closed) => break,
}
}
});
}
pub fn set_max_pending(&self, n: usize) {
self.max_pending.store(n, Ordering::Relaxed);
}
fn grant_of(&self, node: &str) -> Option<Grant> {
let (conn, verified) = self.rt.peer_route(node)?;
let entry = self.grants.get(node).map(|e| e.clone())?;
if !verified || entry.0 != conn {
self.grants.remove(node);
return None;
}
Some(entry.1)
}
fn refusal_allowed(&self, peer: &str) -> bool {
let mut e = self.refusals.entry(peer.to_string()).or_insert((Instant::now(), 0));
if e.0.elapsed() > REFUSAL_WINDOW {
*e = (Instant::now(), 0);
}
e.1 += 1;
e.1 <= REFUSALS
}
fn table(&self) -> Option<&Arc<PlacementTable>> {
self.table.get()
}
fn note(&self, kind: &str, payload: Value) {
if let Some(a) = &self.audit {
a.record(kind, payload);
}
}
pub async fn announce(&self, now_secs: u64) {
for peer in self.rt.peer_ids() {
let _ = self.rt.route_to_remote(&peer, msg(json!({"t": "hello"}))).await;
}
let Some(table) = self.table() else { return };
for ad in table.advertisements(now_secs) {
let Some(role) = ad.name.strip_prefix(super::table::SERVICE_PREFIX) else {
continue;
};
for peer in self.rt.peer_ids() {
let ok = table.mesh_peer_allowed(role, &peer)
&& self.grant_of(&peer).is_some_and(|g| qualifies(&g));
if !ok {
continue;
}
let m = msg(json!({"t": "advert", "ad": ad}));
if let Err(e) = self.rt.route_to_remote(&peer, m).await {
tracing::debug!(peer, error = %e, "infer advert send failed");
}
}
}
}
pub fn qualifying_peers(&self) -> Vec<String> {
let mut v: Vec<String> = self
.rt
.peer_ids()
.into_iter()
.filter(|p| self.grant_of(p).is_some_and(|g| qualifies(&g)))
.collect();
v.sort();
v
}
fn serve(self: &Arc<Self>, ctx: &PeerCtx, id: u64, data: &str) {
let Some(table) = self.table().cloned() else { return };
let grant = ctx.grant();
if grant.is_none() {
return;
}
if !grant.as_ref().is_some_and(qualifies) {
if self.refusal_allowed(&ctx.peer_id) {
self.note(
"infer.mesh.failed",
json!({"peer": ctx.peer_id, "why": "peer does not qualify"}),
);
self.spawn_refusal(ctx.peer_id.clone(), id);
}
return;
}
if !table.peer_listed_any(&ctx.peer_id) {
if self.refusal_allowed(&ctx.peer_id) {
self.note(
"infer.mesh.failed",
json!({"peer": ctx.peer_id, "why": "peer is on no serve allowlist"}),
);
self.spawn_refusal(ctx.peer_id.clone(), id);
}
return;
}
let Ok(permit) = self.intake.clone().try_acquire_owned() else {
if self.refusal_allowed(&ctx.peer_id) {
self.spawn_refusal(ctx.peer_id.clone(), id);
}
return;
};
let Some(payload) = hex_decode(data) else {
return;
};
let frame = MeshFrame {
frame_type: FrameType::InferRequest,
payload,
};
let Ok(first) = frame.encode() else { return };
let hub = self.clone();
let peer = InferPeer {
node_id: ctx.peer_id.clone(),
grant,
};
tokio::spawn(async move {
let mut stream = ServeStream {
rt: hub.rt.clone(),
peer: peer.node_id.clone(),
id,
first: Some(first),
};
let _ = serve_infer(
&mut stream,
&peer,
table.as_ref(),
&hub.upstream,
&hub.gate,
hub.audit.as_deref(),
)
.await;
drop(permit);
});
}
fn spawn_refusal(self: &Arc<Self>, peer: String, id: u64) {
let rt = self.rt.clone();
tokio::spawn(async move {
let payload = super::wire::encode_resp(&super::wire::Resp::Error("refused".into()));
let m = msg(json!({"t": "resp", "id": id, "data": hex_encode(&payload)}));
let _ = rt.route_to_remote(&peer, m).await;
});
}
}
impl PeerControlSink for InferHub {
fn on_peer_control(&self, ctx: &PeerCtx, conn: u64, payload: &Value) -> Vec<Value> {
let first_contact = match ctx.grant() {
Some(g) => self
.grants
.insert(ctx.peer_id.clone(), (conn, g))
.is_none_or(|old| old.0 != conn),
None => {
self.grants.remove(&ctx.peer_id);
false
}
};
let Some(kind) = payload.get("t").and_then(Value::as_str) else {
return Vec::new();
};
match kind {
"hello" if first_contact => return vec![json!({"t": "hello"})],
"hello" => {}
"advert" => {
let Some(table) = self.table() else { return Vec::new() };
if let Some(ad) = payload
.get("ad")
.and_then(|a| serde_json::from_value::<ServiceAdvertisement>(a.clone()).ok())
{
table.ingest_advertisement(&ctx.peer_id, &ad);
}
}
"req" => {
let (Some(id), Some(data)) = (
payload.get("id").and_then(Value::as_u64),
payload.get("data").and_then(Value::as_str),
) else {
return Vec::new();
};
if let Some(me) = self.self_arc() {
me.serve(ctx, id, data);
}
}
"resp" => {
let (Some(id), Some(data)) = (
payload.get("id").and_then(Value::as_u64),
payload.get("data").and_then(Value::as_str),
) else {
return Vec::new();
};
let key = (ctx.peer_id.clone(), id);
if let Some(tx) = self.pending.get(&key).map(|e| e.clone()) {
let delivered = hex_decode(data).is_some_and(|b| tx.try_send(b).is_ok());
if !delivered {
self.pending.remove(&key);
}
}
}
_ => {}
}
Vec::new()
}
}
impl InferHub {
fn self_arc(&self) -> Option<Arc<Self>> {
self.me.get().and_then(std::sync::Weak::upgrade)
}
}
#[async_trait]
impl MeshDialer for InferHub {
fn standing(&self, node_id: &str) -> Option<Grant> {
self.grant_of(node_id)
}
async fn dial(&self, node_id: &str) -> Result<Box<dyn MeshStream>, ProxyError> {
if !self.is_admitted(node_id) {
return Err(ProxyError::Refused(format!("peer {node_id} is not admitted")));
}
if self.pending.len() >= self.max_pending.load(Ordering::Relaxed) {
return Err(ProxyError::NoInstance("too many exchanges in flight".into()));
}
let id = self.next_id.fetch_add(1, Ordering::Relaxed);
let (tx, rx) = mpsc::channel(RESP_BUFFER);
self.pending.insert((node_id.to_string(), id), tx);
Ok(Box::new(ClientStream {
rt: self.rt.clone(),
peer: node_id.to_string(),
id,
rx,
pending: self.pending.clone(),
max_hex: self.limits.max_mesh_request_body.saturating_mul(2) + 4096,
}))
}
}
struct ServeStream {
rt: Arc<MeshRuntime>,
peer: String,
id: u64,
first: Option<Vec<u8>>,
}
#[async_trait]
impl MeshStream for ServeStream {
async fn send(&mut self, data: &[u8]) -> Result<(), MeshError> {
let f = split_frame(data)?;
if f.frame_type != FrameType::InferResponse {
return Err(mesh_err("not a response frame"));
}
let m = msg(json!({"t": "resp", "id": self.id, "data": hex_encode(&f.payload)}));
self.rt.route_to_remote(&self.peer, m).await.map_err(mesh_err)
}
async fn recv(&mut self) -> Result<Vec<u8>, MeshError> {
self.first.take().ok_or(MeshError::ConnectionClosed)
}
async fn close(&mut self) -> Result<(), MeshError> {
Ok(())
}
fn remote_addr(&self) -> Option<SocketAddr> {
None
}
}
struct ClientStream {
rt: Arc<MeshRuntime>,
peer: String,
id: u64,
rx: mpsc::Receiver<Vec<u8>>,
pending: Pending,
max_hex: usize,
}
#[async_trait]
impl MeshStream for ClientStream {
async fn send(&mut self, data: &[u8]) -> Result<(), MeshError> {
let f = split_frame(data)?;
if f.frame_type != FrameType::InferRequest {
return Err(mesh_err("not a request frame"));
}
let hex = hex_encode(&f.payload);
if hex.len() > self.max_hex {
return Err(mesh_err("request too large for the mesh path"));
}
let m = msg(json!({"t": "req", "id": self.id, "data": hex}));
self.rt.route_to_remote(&self.peer, m).await.map_err(mesh_err)
}
async fn recv(&mut self) -> Result<Vec<u8>, MeshError> {
let payload = self.rx.recv().await.ok_or(MeshError::ConnectionClosed)?;
MeshFrame {
frame_type: FrameType::InferResponse,
payload,
}
.encode()
}
async fn close(&mut self) -> Result<(), MeshError> {
self.pending.remove(&(self.peer.clone(), self.id));
Ok(())
}
fn remote_addr(&self) -> Option<SocketAddr> {
None
}
}
impl Drop for ClientStream {
fn drop(&mut self) {
self.pending.remove(&(self.peer.clone(), self.id));
}
}
#[cfg(test)]
impl InferHub {
pub(super) fn grants_len(&self) -> usize {
self.grants.len()
}
}
#[cfg(test)]
pub(super) struct Probe(Pending);
#[cfg(test)]
impl Probe {
pub(super) fn pending_len(&self) -> usize {
self.0.len()
}
}
#[cfg(test)]
impl InferHub {
pub(super) async fn dial_for_test(&self, node: &str) -> TestExchange {
let s = self.dial(node).await.unwrap();
TestExchange { _s: s, probe: Probe(self.pending.clone()) }
}
}
#[cfg(test)]
pub(super) struct TestExchange {
_s: Box<dyn MeshStream>,
probe: Probe,
}
#[cfg(test)]
impl TestExchange {
pub(super) fn pending_len(&self) -> usize {
self.probe.pending_len()
}
}