use std::path::Path;
use std::sync::Arc;
use crate::daemon::audit::{AuditLogger, BUS_STREAM};
pub mod envelope;
pub mod error;
pub mod inbox;
pub mod registry;
pub mod routes;
#[cfg(test)]
mod tests;
pub use envelope::{
BusEdge, BusEnvelope, BusPayload, CallerIdentity, CallerKind, ChannelOrigin, DeliveryState,
Recipient,
};
pub use error::BusError;
pub use inbox::{
CLIENT_INBOX_CAPACITY, ClientInbox, DeliveryOutcome, INBOX_MISS_RECORD, InboxItem, InboxMiss,
InboxSet, InboxSubscription, MissedEnvelope,
};
pub use registry::{InstanceMeta, InstanceRegistry, LiveInstance, PeerTarget};
#[derive(Debug)]
pub struct PeerBus {
registry: InstanceRegistry,
audit: AuditLogger,
}
impl PeerBus {
pub fn new(logs_dir: &Path) -> Self {
Self {
registry: InstanceRegistry::default(),
audit: AuditLogger::for_stream(logs_dir, BUS_STREAM),
}
}
pub fn registry(&self) -> &InstanceRegistry {
&self.registry
}
pub fn log_path(&self) -> &Path {
self.audit.path()
}
pub fn subscribe(&self, instance_id: &str) -> Result<InboxSubscription, BusError> {
Ok(self.registry.resolve_instance(instance_id)?.subscribe())
}
fn verified_sender(&self, mut from: CallerIdentity) -> Result<CallerIdentity, BusError> {
let claimed = from.instance_id.clone().ok_or_else(|| {
BusError::InvalidCaller("peer sender requires instance_id (DOC-60 §6c)".into())
})?;
let live =
self.registry
.resolve_instance(&claimed)
.map_err(|_| BusError::UnregisteredSender {
instance_id: claimed,
})?;
from.definition_id = Some(live.meta.definition_id);
Ok(from)
}
fn record_losses(&self, missed: &[MissedEnvelope]) -> bool {
let mut unrecorded = false;
for loss in missed {
if let Err(e) = self.audit.try_log_record(&loss.record()) {
unrecorded = true;
tracing::error!(
instance_id = %loss.instance_id,
subscription_id = loss.subscription_id,
message_id = %loss.envelope.message_id,
error = %e,
"peer bus could not record an inbox miss; the delivery record \
will be marked losses_unrecorded (#6462)"
);
}
tracing::warn!(
instance_id = %loss.instance_id,
subscription_id = loss.subscription_id,
capacity = CLIENT_INBOX_CAPACITY,
message_id = %loss.envelope.message_id,
missed_total = loss.missed_total,
"peer bus inbox lost an envelope for one client: it must re-read \
the durable log (#6462)"
);
}
unrecorded
}
pub fn publish(
&self,
from: CallerIdentity,
target: &PeerTarget,
payload: BusPayload,
in_reply_to: Option<String>,
) -> Result<BusEnvelope, BusError> {
from.validate()?;
let edge = from.kind.peer_edge()?;
let from = self.verified_sender(from)?;
let live = match self.registry.resolve(target) {
Ok(live) => live,
Err(e) => {
let to = match target {
PeerTarget::Definition(d) => Recipient {
instance_id: None,
definition_id: Some(d.clone()),
},
PeerTarget::Instance(i) => Recipient {
instance_id: Some(i.clone()),
definition_id: None,
},
};
self.audit.log_record(&BusEnvelope::new(
edge,
from,
to,
payload,
in_reply_to,
DeliveryState::Dropped,
));
return Err(e);
}
};
let mut envelope = BusEnvelope::new(
edge,
from,
Recipient {
instance_id: Some(live.meta.instance_id.clone()),
definition_id: Some(live.meta.definition_id.clone()),
},
payload,
in_reply_to,
DeliveryState::Delivered,
);
match live.deliver(envelope.clone()) {
DeliveryOutcome::Delivered { missed } => {
let losses_unrecorded = self.record_losses(&missed);
self.audit
.log_record(&EnvelopeRecord::new(&envelope, losses_unrecorded));
Ok(envelope)
}
DeliveryOutcome::NoSubscriber => {
envelope.delivery_state = DeliveryState::Dropped;
self.audit.log_record(&envelope);
Err(BusError::NoSubscriber {
instance_id: live.meta.instance_id,
})
}
}
}
}
#[derive(Debug, serde::Serialize)]
pub struct EnvelopeRecord<'a> {
#[serde(flatten)]
pub envelope: &'a BusEnvelope,
#[serde(skip_serializing_if = "std::ops::Not::not")]
pub losses_unrecorded: bool,
}
impl<'a> EnvelopeRecord<'a> {
pub fn new(envelope: &'a BusEnvelope, losses_unrecorded: bool) -> Self {
Self {
envelope,
losses_unrecorded,
}
}
}
pub type SharedBus = Arc<PeerBus>;