flybrain/services/flysim/crates/flybus/src/router/state.rs
acamilo 093db147eb feat(flybus): add standalone IPC bus and conformance tests
(cherry picked from commit 95ac8d1bdbc866a7c49fad3ddba468d945267650)
2026-09-22 03:46:27 +00:00

2606 lines
92 KiB
Rust

//! The router's state machine: every routing, queueing and ownership decision, with no I/O.
//!
//! One `State` sits behind one mutex. Connection tasks call in with a decoded command, get
//! their reply queued on the connection's control lane, and do any file I/O (staging
//! creation, seal copies, unlinks) only after the lock is released. Writers pull their next
//! frame from here, so a delivery is marked dispatched and assigned its delivery id at the
//! moment its bytes are about to be written, never earlier.
//!
//! Ownership: every sealed artifact has a root count. A root is held by each queued delivery,
//! each delivery handed to a client and not yet consumed, each explicit hold and each retained
//! topic value that references it; a delivery naming the same artifact twice holds one root.
//! The last root to go collects the artifact: its bytes leave the quota and its file is
//! unlinked. Staging writers are not roots; an unsealed artifact is owned by its writer alone.
use std::collections::{HashMap, HashSet, VecDeque};
use std::sync::Arc;
use serde_json::{Map, Value};
use tokio::sync::{Notify, watch};
use crate::error::{BusError, Dispatch, ErrorCode};
use crate::limits::Limits;
use crate::policy::{Grants, Policy};
use crate::store::{sealed_rel, staging_rel};
use crate::wire::{
ArtifactRef, Attachment, Envelope, Fields, GENERATION, Identity, Kind, MAJOR, MAX_BATCH,
MAX_CREDIT, MAX_ENVELOPE_BYTES, MINOR, WireError, id_batch, parse_serial_id, serial_id,
};
pub(crate) type ConnKey = u64;
type CallKey = u64;
/// After this many consecutive control or RPC frames while topic data waits, one topic frame
/// goes out, so telemetry is deprioritised but never starved outright.
const TOPIC_STARVATION_LIMIT: u32 = 16;
/// Placeholder that is as long as any router-issued serial id can be.
const MAX_SERIAL: u64 = u64::MAX;
/// Signals between a connection's tasks and the state.
pub(crate) struct ConnSignals {
/// Wakes the writer: something may be ready to send.
pub wake: Notify,
/// Flips to true when the connection is closed; both tasks watch it.
pub shutdown: watch::Sender<bool>,
/// Serializes synchronous transport polls with connection teardown. It is never held
/// across an await.
pub write_gate: std::sync::Mutex<WriteGate>,
/// The last frames to write before closing: a refusal or `connection.closing` notice,
/// preceded on router shutdown by `subscription.closed` notices.
pub final_frames: std::sync::Mutex<Vec<Vec<u8>>>,
}
#[derive(Default)]
pub(crate) struct WriteGate {
closing: bool,
frame_len: usize,
written: usize,
cut_partial: bool,
}
impl WriteGate {
pub(crate) fn begin_frame(&mut self, len: usize) -> bool {
if self.closing {
return false;
}
self.frame_len = len;
self.written = 0;
true
}
pub(crate) fn wrote(&mut self, len: usize) {
self.written += len;
debug_assert!(self.written <= self.frame_len);
}
pub(crate) fn finish_frame(&mut self) {
if !self.closing {
debug_assert_eq!(self.written, self.frame_len);
self.frame_len = 0;
self.written = 0;
}
}
fn begin_close(&mut self) {
self.closing = true;
self.cut_partial = self.written > 0 && self.written < self.frame_len;
}
pub(crate) fn closing(&self) -> bool {
self.closing
}
pub(crate) fn cut_partial(&self) -> bool {
self.cut_partial
}
}
impl ConnSignals {
pub(crate) fn new() -> ConnSignals {
ConnSignals {
wake: Notify::new(),
shutdown: watch::Sender::new(false),
write_gate: std::sync::Mutex::new(WriteGate::default()),
final_frames: std::sync::Mutex::new(Vec::new()),
}
}
}
/// What the connection task must do after `handle` returns.
pub(crate) enum Outcome {
Done,
/// Create the staging file, then call `finish_allocate`.
Allocate(AllocJob),
/// Copy staging into the sealed file (off the routing path), then call `finish_seal`.
Seal(SealJob),
/// The connection was closed; stop reading.
Close,
}
pub(crate) struct AllocJob {
pub conn: ConnKey,
pub reply_to: String,
pub serial: u64,
pub len: u64,
pub owner: u64,
}
pub(crate) struct SealJob {
pub conn: ConnKey,
pub reply_to: String,
pub serial: u64,
pub len: u64,
pub digest: Option<String>,
pub owner: u64,
}
pub(crate) enum NextFrame {
Frame(Vec<u8>),
Idle,
Gone,
}
pub(crate) struct Outbound {
kind: Kind,
op: String,
reply_to: Option<String>,
body: Map<String, Value>,
attachments: Vec<Attachment>,
/// Conservative encoded size using the longest router envelope id.
bytes: usize,
}
/// Registry counters, for tests and resource measurement. They are logical: an unlinked file
/// a process still has open keeps its pages until that process closes it.
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct RouterStats {
pub connections: usize,
pub services: usize,
pub topics: usize,
pub subscriptions: usize,
/// Call correlation records the router still keeps, detached ones included.
pub calls: usize,
/// Calls counted against their callers' active-call limits (admitted, not finished,
/// not detached).
pub active_calls: usize,
/// Artifacts in any state: writing, sealing or sealed.
pub artifacts: usize,
pub sealed_artifacts: usize,
/// Sum of root counts over sealed artifacts.
pub artifact_roots: u64,
/// Live owners (deliveries, holds, writers) over all connections.
pub owners: usize,
/// Live request reply capabilities. They are independent of delivery credit but share the
/// per-client bound.
pub reply_capabilities: usize,
/// Admitted and not yet dispatched: topic messages in subscription queues, calls in
/// service queues and results waiting for their caller.
pub queued: usize,
/// Staging, sealing-copy and sealed bytes currently charged to the store.
pub store_bytes: u64,
pub retained_bytes: u64,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
enum OwnerKey {
Delivery(u64),
Hold(u64),
}
enum Owner {
Writer {
artifact: u64,
sealing: bool,
},
Hold {
artifact: u64,
},
Delivery {
artifacts: Vec<u64>,
kind: DeliveryKind,
},
}
impl Owner {
/// Counted against the ordinary owner budget (everything but RPC deliveries).
fn ordinary(&self) -> bool {
!matches!(
self,
Owner::Delivery {
kind: DeliveryKind::Request { .. } | DeliveryKind::Result { .. },
..
}
)
}
}
enum DeliveryKind {
Topic {
sub: u64,
},
Request {
call: CallKey,
service: String,
incarnation: String,
},
Result {
call: CallKey,
},
}
struct Hello {
identity: Identity,
grants: Grants,
}
struct Conn {
signals: Arc<ConnSignals>,
connection_id: String,
hello: Option<Hello>,
expected_client_id: Option<String>,
last_command: Option<u64>,
out_serial: u64,
delivery_issued: u64,
hold_issued: u64,
sub_issued: u64,
owners: HashMap<OwnerKey, Owner>,
ordinary_owners: usize,
subs: HashMap<u64, Sub>,
sub_order: Vec<u64>,
sub_cursor: usize,
services: Vec<String>,
svc_cursor: usize,
/// Request delivery serial -> call, for `rpc.reply` correlation.
requests: HashMap<u64, CallKey>,
reply_capabilities: usize,
call_watermark: Option<u64>,
/// Admitted calls not yet finished or detached, by call serial.
active_calls: HashMap<u64, CallKey>,
result_queue: VecDeque<CallKey>,
control: VecDeque<Outbound>,
control_bytes: usize,
streak: u32,
queued_bytes: usize,
}
struct Art {
state: ArtState,
byte_length: u64,
content_type: String,
digest: Option<String>,
roots: u64,
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum ArtState {
Writing,
Sealing,
Sealed,
}
struct TopicMsg {
topic: String,
topic_incarnation: String,
sequence: u64,
payload: Map<String, Value>,
attachments: Vec<(String, ArtifactRef)>,
artifacts: Vec<u64>,
bytes: usize,
artifact_bytes: u64,
}
struct Sub {
topic: String,
topic_incarnation: String,
latest: bool,
max_queued: u64,
max_in_flight: u64,
in_flight: u64,
queue: VecDeque<Arc<TopicMsg>>,
replaced: u64,
}
struct Topic {
incarnation: String,
retain: bool,
sequence: u64,
retained: Option<Arc<TopicMsg>>,
subscribers: Vec<(ConnKey, u64)>,
}
struct Svc {
incarnation: String,
conn: ConnKey,
max_queued: u64,
max_in_flight: u64,
in_flight: u64,
queue: VecDeque<CallKey>,
}
struct ResultMsg {
responder: Identity,
outcome: Map<String, Value>,
attachments: Vec<(String, ArtifactRef)>,
artifacts: Vec<u64>,
}
enum Phase {
Queued,
Dispatched { delivery: u64 },
Replied { delivery: u64, result: CallResult },
}
enum CallResult {
Queued(ResultMsg),
Delivered,
}
struct Call {
caller: ConnKey,
caller_identity: Identity,
call_serial: u64,
call_id: String,
target: String,
service_incarnation: String,
service_conn: ConnKey,
method: String,
payload: Map<String, Value>,
attachments: Vec<(String, ArtifactRef)>,
artifacts: Vec<u64>,
phase: Phase,
detached: bool,
reply_capability: bool,
}
#[derive(Default)]
struct ClientRecord {
live: Option<ConnKey>,
last_incarnation: Option<String>,
}
pub(crate) struct State {
pub router_id: String,
pub store_id: String,
contract_digest: String,
limits: Limits,
policy: Policy,
closed: bool,
conns: HashMap<ConnKey, Conn>,
identities: HashMap<String, ClientRecord>,
services: HashMap<String, Svc>,
topics: HashMap<String, Topic>,
artifacts: HashMap<u64, Art>,
calls: HashMap<CallKey, Call>,
next_conn: u64,
next_service: u64,
next_topic: u64,
next_artifact: u64,
next_call: u64,
store_bytes: u64,
retained_bytes: u64,
subscriptions: usize,
unlinks: Vec<String>,
notices: Vec<(ConnKey, &'static str, Map<String, Value>)>,
}
fn obj(pairs: Vec<(&str, Value)>) -> Map<String, Value> {
pairs.into_iter().map(|(k, v)| (k.to_owned(), v)).collect()
}
fn err<T>(code: ErrorCode, message: impl Into<String>) -> Result<T, BusError> {
Err(BusError::new(code, message))
}
fn wire(e: WireError) -> BusError {
e.into()
}
fn dedup(serials: Vec<u64>) -> Vec<u64> {
let mut seen = HashSet::new();
serials.into_iter().filter(|s| seen.insert(*s)).collect()
}
fn attachments_with_owner(list: &[(String, ArtifactRef)], owner: &str) -> Vec<Attachment> {
list.iter()
.map(|(name, r)| Attachment {
name: name.clone(),
reference: r.clone(),
owner_id: owner.to_owned(),
})
.collect()
}
fn frame_len(
kind: Kind,
op: &str,
body: Map<String, Value>,
attachments: Vec<Attachment>,
) -> usize {
let mut env = Envelope::new(serial_id("bus", MAX_SERIAL), kind, op, body);
env.attachments = attachments;
serde_json::to_vec(&env.to_value())
.map(|b| b.len())
.unwrap_or(usize::MAX)
}
impl Call {
fn request_body(&self, delivery_id: &str) -> Map<String, Value> {
obj(vec![
("deliveryId", delivery_id.into()),
("callId", self.call_id.clone().into()),
("caller", self.caller_identity.to_json()),
("target", self.target.clone().into()),
(
"serviceIncarnation",
self.service_incarnation.clone().into(),
),
("method", self.method.clone().into()),
("payload", Value::Object(self.payload.clone())),
])
}
fn result_body(&self, delivery_id: &str, result: &ResultMsg) -> Map<String, Value> {
obj(vec![
("deliveryId", delivery_id.into()),
("callId", self.call_id.clone().into()),
("responder", result.responder.to_json()),
(
"serviceIncarnation",
self.service_incarnation.clone().into(),
),
("outcome", Value::Object(result.outcome.clone())),
])
}
}
impl TopicMsg {
fn body(&self, delivery_id: &str, subscription_id: &str, replaced: u64) -> Map<String, Value> {
obj(vec![
("deliveryId", delivery_id.into()),
("subscriptionId", subscription_id.into()),
("topic", self.topic.clone().into()),
("topicIncarnation", self.topic_incarnation.clone().into()),
("topicSequence", self.sequence.to_string().into()),
("replaced", replaced.to_string().into()),
("payload", Value::Object(self.payload.clone())),
])
}
}
impl State {
pub(crate) fn new(
router_id: String,
store_id: String,
contract_digest: String,
limits: Limits,
policy: Policy,
) -> State {
State {
router_id,
store_id,
contract_digest,
limits,
policy,
closed: false,
conns: HashMap::new(),
identities: HashMap::new(),
services: HashMap::new(),
topics: HashMap::new(),
artifacts: HashMap::new(),
calls: HashMap::new(),
next_conn: 0,
next_service: 0,
next_topic: 0,
next_artifact: 0,
next_call: 0,
store_bytes: 0,
retained_bytes: 0,
subscriptions: 0,
unlinks: Vec::new(),
notices: Vec::new(),
}
}
/// Store files (relative paths) to unlink once the lock is released.
pub(crate) fn take_unlinks(&mut self) -> Vec<String> {
std::mem::take(&mut self.unlinks)
}
pub(crate) fn stats(&self) -> RouterStats {
RouterStats {
connections: self.conns.len(),
services: self.services.len(),
topics: self.topics.len(),
subscriptions: self.subscriptions,
calls: self.calls.len(),
active_calls: self.conns.values().map(|c| c.active_calls.len()).sum(),
artifacts: self.artifacts.len(),
sealed_artifacts: self
.artifacts
.values()
.filter(|a| a.state == ArtState::Sealed)
.count(),
artifact_roots: self.artifacts.values().map(|a| a.roots).sum(),
owners: self.conns.values().map(|c| c.owners.len()).sum(),
reply_capabilities: self.conns.values().map(|c| c.reply_capabilities).sum(),
queued: self
.conns
.values()
.map(|c| {
c.subs.values().map(|s| s.queue.len()).sum::<usize>() + c.result_queue.len()
})
.sum::<usize>()
+ self.services.values().map(|s| s.queue.len()).sum::<usize>(),
store_bytes: self.store_bytes,
retained_bytes: self.retained_bytes,
}
}
// -----------------------------------------------------------------------------------------
// Connections
pub(crate) fn add_conn(
&mut self,
signals: Arc<ConnSignals>,
expected_client_id: Option<String>,
) -> Option<ConnKey> {
if self.closed
|| self.conns.len() >= self.limits.max_clients
|| (expected_client_id.is_none() && !self.policy.permits_unbound_transport())
{
return None;
}
self.next_conn += 1;
let key = self.next_conn;
self.conns.insert(
key,
Conn {
signals,
connection_id: serial_id("conn", key),
hello: None,
expected_client_id,
last_command: None,
out_serial: 0,
delivery_issued: 0,
hold_issued: 0,
sub_issued: 0,
owners: HashMap::new(),
ordinary_owners: 0,
subs: HashMap::new(),
sub_order: Vec::new(),
sub_cursor: 0,
services: Vec::new(),
svc_cursor: 0,
requests: HashMap::new(),
reply_capabilities: 0,
call_watermark: None,
active_calls: HashMap::new(),
result_queue: VecDeque::new(),
control: VecDeque::new(),
control_bytes: 0,
streak: 0,
queued_bytes: 0,
},
);
Some(key)
}
fn wake(&self, c: ConnKey) {
if let Some(conn) = self.conns.get(&c) {
conn.signals.wake.notify_one();
}
}
/// Builds a router-originated item without assigning its envelope id. IDs are assigned only
/// after the scheduler selects an item, so fairness cannot reorder numbered envelopes.
fn outbound(
&self,
kind: Kind,
op: &str,
reply_to: Option<String>,
body: Map<String, Value>,
attachments: Vec<Attachment>,
) -> Option<Outbound> {
let mut env = Envelope::new(serial_id("bus", MAX_SERIAL), kind, op, body.clone());
env.reply_to = reply_to;
env.attachments = attachments.clone();
let bytes = env.encode().ok()?.len();
Some(Outbound {
kind,
op: op.to_owned(),
reply_to: env.reply_to,
body,
attachments,
bytes,
})
}
fn encode_outbound(&mut self, c: ConnKey, item: Outbound) -> Option<Vec<u8>> {
let conn = self.conns.get_mut(&c)?;
conn.out_serial += 1;
let mut env = Envelope::new(
serial_id("bus", conn.out_serial),
item.kind,
&item.op,
item.body,
);
env.reply_to = item.reply_to;
env.attachments = item.attachments;
env.encode().ok()
}
/// Queues a reply or notice on the control lane; closes the connection if the lane is full.
fn push_control(&mut self, c: ConnKey, frame: Outbound) {
let (frames, bytes) = (
self.limits.max_control_frames,
self.limits.max_control_bytes,
);
let Some(conn) = self.conns.get_mut(&c) else {
return;
};
if conn.control.len() >= frames || conn.control_bytes + frame.bytes > bytes {
let body = obj(vec![
("code", ErrorCode::QuotaExceeded.as_str().into()),
(
"message",
"control lane exhausted: replies and notices are not being read".into(),
),
]);
let last = self.outbound(Kind::Notice, "connection.closing", None, body, Vec::new());
self.close_conn(c, last);
return;
}
conn.control_bytes += frame.bytes;
conn.control.push_back(frame);
conn.signals.wake.notify_one();
}
fn reply(
&mut self,
c: ConnKey,
reply_to: &str,
op: &str,
result: Result<Map<String, Value>, BusError>,
) {
let body = reply_body(result);
if let Some(frame) =
self.outbound(Kind::Reply, op, Some(reply_to.to_owned()), body, Vec::new())
{
self.push_control(c, frame);
}
}
fn notice(&mut self, c: ConnKey, op: &'static str, body: Map<String, Value>) {
self.notices.push((c, op, body));
}
/// Sends deferred notices. Pushing one may close its connection, whose cleanup may defer
/// more, so this loops until none are left.
fn flush_notices(&mut self) {
while !self.notices.is_empty() {
for (c, op, body) in std::mem::take(&mut self.notices) {
if let Some(frame) = self.outbound(Kind::Notice, op, None, body, Vec::new()) {
self.push_control(c, frame);
}
}
}
}
/// Closes `c`: records the final frames, signals both tasks and releases everything the
/// connection owned. Replies still queued on its control lane are dropped.
pub(crate) fn close_conn(
&mut self,
c: ConnKey,
final_frames: impl IntoIterator<Item = Outbound>,
) {
let Some(conn) = self.conns.get(&c) else {
return;
};
let signals = conn.signals.clone();
let final_frames = final_frames
.into_iter()
.filter_map(|frame| self.encode_outbound(c, frame));
signals
.final_frames
.lock()
.unwrap_or_else(|e| e.into_inner())
.extend(final_frames);
// A transport poll that is already in progress finishes before this lock is acquired.
// Once acquired, teardown marks the stream closing before reclaiming any owner, and no
// later normal-frame poll is allowed through.
let mut write_gate = signals.write_gate.lock().unwrap_or_else(|e| e.into_inner());
write_gate.begin_close();
self.disconnect(c);
signals.shutdown.send_replace(true);
signals.wake.notify_one();
drop(write_gate);
self.flush_notices();
}
fn violation(&mut self, c: ConnKey, message: String) -> Outcome {
let body = obj(vec![
("code", ErrorCode::InvalidEnvelope.as_str().into()),
("message", message.into()),
]);
let last = self.outbound(Kind::Notice, "connection.closing", None, body, Vec::new());
self.close_conn(c, last);
Outcome::Close
}
/// A frame that failed to decode: close with a notice.
pub(crate) fn reject_frame(&mut self, c: ConnKey, message: String) {
self.violation(c, message);
}
/// Closes every connection and refuses new ones.
pub(crate) fn shutdown(&mut self) {
self.closed = true;
let keys: Vec<ConnKey> = self.conns.keys().copied().collect();
for c in keys {
let mut finals = Vec::new();
let subs: Vec<(u64, String, String)> = self.conns[&c]
.subs
.iter()
.map(|(id, s)| (*id, s.topic.clone(), s.topic_incarnation.clone()))
.collect();
for (id, topic, inc) in subs {
let body = obj(vec![
("subscriptionId", serial_id("sub", id).into()),
("topic", topic.into()),
("topicIncarnation", inc.into()),
("reason", "router-stopping".into()),
]);
finals.extend(self.outbound(
Kind::Notice,
"subscription.closed",
None,
body,
Vec::new(),
));
}
let body = obj(vec![
("code", ErrorCode::RouterLost.as_str().into()),
("message", "router stopping".into()),
]);
finals.extend(self.outbound(
Kind::Notice,
"connection.closing",
None,
body,
Vec::new(),
));
self.close_conn(c, finals);
}
let topics: Vec<String> = self.topics.keys().cloned().collect();
for t in topics {
if let Some(msg) = self.topics.get_mut(&t).and_then(|t| t.retained.take()) {
self.retained_bytes -= msg.artifact_bytes;
self.drop_roots(&msg.artifacts);
}
}
}
/// Releases everything `c` owned. Retained topic values stay; they are router-owned.
pub(crate) fn disconnect(&mut self, c: ConnKey) {
let Some(conn) = self.conns.get(&c) else {
return;
};
if let Some(h) = &conn.hello
&& let Some(rec) = self.identities.get_mut(&h.identity.client_id)
&& rec.live == Some(c)
{
rec.live = None;
}
// Services: queued calls fail; dispatched ones are settled with the owners below.
for name in conn.services.clone() {
self.remove_service(&name, "disconnected");
}
// Subscriptions.
let conn = self.conns.get_mut(&c).expect("present");
let subs: Vec<u64> = conn.sub_order.clone();
for id in subs {
self.drop_subscription(c, id);
}
// Owners.
let conn = self.conns.get_mut(&c).expect("present");
let owners: Vec<(OwnerKey, Owner)> = conn.owners.drain().collect();
conn.ordinary_owners = 0;
for (_, owner) in owners {
self.release_owner(c, owner);
}
// Calls dispatched to this connection that it never answered, whether or not it still
// held the request: the caller learns the call may have executed.
let served: Vec<CallKey> = self
.calls
.iter()
.filter(|(_, call)| {
call.service_conn == c && matches!(call.phase, Phase::Dispatched { .. })
})
.map(|(k, _)| *k)
.collect();
for key in served {
let call = &self.calls[&key];
if !call.detached {
let body = obj(vec![
("callId", call.call_id.clone().into()),
("code", ErrorCode::NoService.as_str().into()),
("message", "the service disconnected before replying".into()),
("dispatch", Dispatch::Dispatched.as_str().into()),
]);
let caller = call.caller;
self.notice(caller, "call.failed", body);
}
self.remove_call(key);
}
// Calls this connection made.
let mine: Vec<CallKey> = self
.calls
.iter()
.filter(|(_, call)| call.caller == c)
.map(|(k, _)| *k)
.collect();
for key in mine {
let Some(call) = self.calls.get_mut(&key) else {
continue;
};
match call.phase {
Phase::Queued => {
let (target, inc) = (call.target.clone(), call.service_incarnation.clone());
if let Some(svc) = self
.services
.get_mut(&target)
.filter(|s| s.incarnation == inc)
{
svc.queue.retain(|k| *k != key);
}
self.remove_call(key);
}
// The service may still reply (it will hear `routed:false`) or consume the
// request; the record goes when either happens.
Phase::Dispatched { .. } => {
call.detached = true;
if !call.reply_capability {
self.remove_call(key);
}
}
Phase::Replied { .. } => self.remove_call(key),
}
}
if let Some(conn) = self.conns.remove(&c) {
debug_assert!(conn.owners.is_empty());
}
}
// -----------------------------------------------------------------------------------------
// Roots and artifacts
fn add_roots(&mut self, serials: &[u64]) {
for s in serials {
if let Some(a) = self.artifacts.get_mut(s) {
a.roots += 1;
}
}
}
fn drop_roots(&mut self, serials: &[u64]) {
for s in serials {
let Some(a) = self.artifacts.get_mut(s) else {
continue;
};
a.roots -= 1;
if a.roots == 0 && a.state == ArtState::Sealed {
let a = self.artifacts.remove(s).expect("present");
self.store_bytes -= a.byte_length;
self.unlinks.push(sealed_rel(*s));
}
}
}
fn reference(&self, serial: u64) -> Option<ArtifactRef> {
let a = self.artifacts.get(&serial)?;
Some(ArtifactRef {
store_id: self.store_id.clone(),
artifact_id: serial_id("a", serial),
generation: GENERATION,
byte_length: a.byte_length,
content_type: a.content_type.clone(),
digest: a.digest.clone(),
})
}
/// Drops an unsealed writer's artifact and its staging reservation.
fn abandon_writer(&mut self, serial: u64) {
if let Some(a) = self.artifacts.remove(&serial) {
self.store_bytes -= a.byte_length;
self.unlinks.push(staging_rel(serial));
}
}
fn take_owner(&mut self, c: ConnKey, key: OwnerKey) -> Option<Owner> {
let conn = self.conns.get_mut(&c)?;
let owner = conn.owners.remove(&key)?;
if owner.ordinary() {
conn.ordinary_owners -= 1;
}
Some(owner)
}
fn insert_owner(&mut self, c: ConnKey, key: OwnerKey, owner: Owner) {
let conn = self.conns.get_mut(&c).expect("present");
if owner.ordinary() {
conn.ordinary_owners += 1;
}
conn.owners.insert(key, owner);
}
fn ordinary_budget_left(&self, c: ConnKey) -> bool {
let conn = &self.conns[&c];
conn.ordinary_owners
< self.limits.max_owners_per_client - self.limits.reserved_owners_per_client
&& conn.owners.len() < self.limits.max_owners_per_client
}
/// Releases a removed owner's roots and returns its credit.
fn release_owner(&mut self, c: ConnKey, owner: Owner) {
match owner {
Owner::Hold { artifact } => self.drop_roots(&[artifact]),
// A sealing writer's artifact is reclaimed by `finish_seal`, which finds its owner gone.
Owner::Writer {
artifact,
sealing: false,
} => self.abandon_writer(artifact),
Owner::Writer { sealing: true, .. } => {}
Owner::Delivery { artifacts, kind } => {
self.drop_roots(&artifacts);
match kind {
DeliveryKind::Topic { sub } => {
if let Some(s) = self
.conns
.get_mut(&c)
.and_then(|conn| conn.subs.get_mut(&sub))
{
s.in_flight -= 1;
}
}
DeliveryKind::Request {
call,
service,
incarnation,
} => {
if let Some(svc) = self
.services
.get_mut(&service)
.filter(|s| s.incarnation == incarnation)
{
svc.in_flight -= 1;
}
// A detached call without a remaining reply capability is terminal.
if self
.calls
.get(&call)
.is_some_and(|e| e.detached && !e.reply_capability)
{
self.remove_call(call);
}
}
DeliveryKind::Result { call } => self.remove_call(call),
}
}
}
self.wake(c);
}
/// Checks that `owner_id` is a live owner on `c` holding the sealed artifact `r`.
fn check_owned(&self, c: ConnKey, r: &ArtifactRef, owner_id: &str) -> Result<u64, BusError> {
if r.store_id != self.store_id {
return err(
ErrorCode::ArtifactGone,
"reference from another store incarnation",
);
}
let Some(serial) = parse_serial_id("a", &r.artifact_id) else {
return err(ErrorCode::ArtifactGone, "unknown artifact id");
};
let key = if let Some(n) = parse_serial_id("dlv", owner_id) {
OwnerKey::Delivery(n)
} else if let Some(n) = parse_serial_id("own", owner_id) {
OwnerKey::Hold(n)
} else {
return err(ErrorCode::OwnerInvalid, "not an owner id");
};
let conn = &self.conns[&c];
let holds = match conn.owners.get(&key) {
None => {
return err(
ErrorCode::OwnerInvalid,
format!("{owner_id} is not a live owner on this connection"),
);
}
Some(Owner::Writer { artifact, .. }) if *artifact == serial => {
return err(ErrorCode::ArtifactUnsealed, "artifact is not sealed");
}
Some(Owner::Writer { .. }) => false,
Some(Owner::Hold { artifact }) => *artifact == serial,
Some(Owner::Delivery { artifacts, .. }) => artifacts.contains(&serial),
};
if !holds {
return err(
ErrorCode::OwnerInvalid,
format!("{owner_id} does not own {}", r.artifact_id),
);
}
let Some(art) = self.artifacts.get(&serial) else {
return err(ErrorCode::ArtifactGone, "artifact was collected");
};
if art.state != ArtState::Sealed {
return err(ErrorCode::ArtifactUnsealed, "artifact is not sealed");
}
if self.reference(serial).as_ref() != Some(r) {
return err(
ErrorCode::ArtifactMismatch,
"reference does not match the artifact",
);
}
Ok(serial)
}
/// Validates every attachment and returns the distinct artifacts, in first-seen order.
fn check_attachments(&self, c: ConnKey, list: &[Attachment]) -> Result<Vec<u64>, BusError> {
let mut serials = Vec::with_capacity(list.len());
for a in list {
serials.push(self.check_owned(c, &a.reference, &a.owner_id)?);
}
Ok(dedup(serials))
}
// -----------------------------------------------------------------------------------------
// Commands
pub(crate) fn handle(&mut self, c: ConnKey, env: Envelope) -> Outcome {
let Some(conn) = self.conns.get_mut(&c) else {
return Outcome::Close;
};
if env.kind != Kind::Command || env.reply_to.is_some() {
return self.violation(c, "clients send commands only, with a null replyTo".into());
}
let Some(serial) = parse_serial_id("msg", &env.id) else {
return self.violation(c, "command ids are msg-<U64>".into());
};
if conn.last_command.is_some_and(|last| serial <= last) {
return self.violation(c, "command ids must increase".into());
}
conn.last_command = Some(serial);
if conn.hello.is_none() {
if env.op != "bus.hello" {
return self.violation(c, "the first command must be bus.hello".into());
}
return self.hello(c, env);
}
if env.op == "bus.hello" {
return self.violation(c, "bus.hello was already negotiated".into());
}
if env.major != MAJOR || env.minor != MINOR {
return self.violation(
c,
format!("envelopes on this connection are {MAJOR}.{MINOR}"),
);
}
let op = env.op.clone();
let result = if !env.attachments.is_empty()
&& !matches!(op.as_str(), "rpc.call" | "rpc.reply" | "publish")
{
err(
ErrorCode::InvalidEnvelope,
format!("{op} takes no attachments"),
)
} else {
match op.as_str() {
"service.register" => self.op_register(c, &env),
"service.unregister" => self.op_unregister(c, &env),
"rpc.call" => self.op_call(c, &env),
"rpc.reply" => self.op_reply(c, &env),
"rpc.responder.release" => self.op_responder_release(c, &env),
"rpc.cancel" => self.op_cancel(c, &env),
"topic.declare" => self.op_declare(c, &env),
"topic.clear" => self.op_clear(c, &env),
"topic.delete" => self.op_delete(c, &env),
"subscribe" => self.op_subscribe(c, &env),
"unsubscribe" => self.op_unsubscribe(c, &env),
"publish" => self.op_publish(c, &env),
"delivery.consumed" => self.op_consumed(c, &env),
"artifact.release" => self.op_release(c, &env),
"artifact.open" => self.op_open(c, &env),
"artifact.retain" => self.op_retain(c, &env),
"artifact.allocate" => match self.op_allocate(c, &env) {
Ok(job) => return Outcome::Allocate(job),
Err(e) => Err(e),
},
"artifact.seal" => match self.op_seal(c, &env) {
Ok(job) => return Outcome::Seal(job),
Err(e) => Err(e),
},
_ => err(
ErrorCode::InvalidEnvelope,
format!("unknown operation {op:?}"),
),
}
};
self.reply(c, &env.id, &op, result);
self.flush_notices();
if self.conns.contains_key(&c) {
Outcome::Done
} else {
Outcome::Close
}
}
fn grants(&self, c: ConnKey) -> &Grants {
&self.conns[&c].hello.as_ref().expect("after hello").grants
}
fn identity(&self, c: ConnKey) -> Identity {
self.conns[&c]
.hello
.as_ref()
.expect("after hello")
.identity
.clone()
}
fn hello(&mut self, c: ConnKey, env: Envelope) -> Outcome {
let parsed = (|| -> Result<(String, String, Vec<u64>), WireError> {
let mut f = Fields::of(&env.body, "bus.hello");
let client_id = f.id("clientId")?;
let incarnation = f.id("clientIncarnation")?;
let majors = f.array("supportedMajors", 1, 16)?;
let mut out = Vec::new();
for m in majors {
match m.as_u64() {
Some(n) if (1..=MAX_CREDIT).contains(&n) => out.push(n),
_ => {
return Err(WireError(
"bus.hello: supportedMajors holds integers 1..=65535".into(),
));
}
}
}
f.finish()?;
Ok((client_id, incarnation, out))
})();
let refuse = |s: &mut State, e: BusError| {
let body = reply_body(Err(e));
let last = s.outbound(
Kind::Reply,
"bus.hello",
Some(env.id.clone()),
body,
Vec::new(),
);
s.close_conn(c, last);
Outcome::Close
};
let (client_id, incarnation, majors) = match parsed {
Ok(p) => p,
Err(e) => return refuse(self, e.into()),
};
if !env.attachments.is_empty() {
return refuse(self, BusError::invalid("bus.hello takes no attachments"));
}
if !majors.contains(&MAJOR) {
return refuse(
self,
BusError::new(
ErrorCode::VersionMismatch,
format!("this router speaks major {MAJOR} only"),
),
);
}
let Some(grants) = self.policy.grants_for(&client_id) else {
return refuse(
self,
BusError::new(ErrorCode::NotAuthorized, "client id is not configured"),
);
};
let rec = self.identities.entry(client_id.clone()).or_default();
if rec.live.is_some() {
return refuse(
self,
BusError::new(ErrorCode::NotAuthorized, "client id is already connected"),
);
}
if rec.last_incarnation.as_deref() == Some(incarnation.as_str()) {
return refuse(
self,
BusError::new(
ErrorCode::NotAuthorized,
"client incarnation was already used",
),
);
}
if let Some(expected) = self.conns[&c].expected_client_id.as_deref()
&& expected != client_id
{
return refuse(
self,
BusError::new(
ErrorCode::NotAuthorized,
"client id does not match the launcher's transport binding",
),
);
}
let rec = self.identities.get_mut(&client_id).expect("inserted");
rec.live = Some(c);
rec.last_incarnation = Some(incarnation.clone());
let identity = Identity {
client_id,
client_incarnation: incarnation,
};
let conn = self.conns.get_mut(&c).expect("present");
conn.hello = Some(Hello { identity, grants });
let value = obj(vec![
("routerId", self.router_id.clone().into()),
("connectionId", conn.connection_id.clone().into()),
("selectedMajor", MAJOR.into()),
("selectedMinor", MINOR.into()),
("contractDigest", self.contract_digest.clone().into()),
("limits", self.limits.to_json()),
]);
self.reply(c, &env.id, "bus.hello", Ok(value));
if self.conns.contains_key(&c) {
Outcome::Done
} else {
Outcome::Close
}
}
// ---- services and RPC
fn op_register(&mut self, c: ConnKey, env: &Envelope) -> Result<Map<String, Value>, BusError> {
let mut f = Fields::of(&env.body, "service.register");
let name = f.name("name").map_err(wire)?;
let max_queued = f.int("maxQueued", 1, MAX_CREDIT).map_err(wire)?;
let max_in_flight = f.int("maxInFlight", 1, MAX_CREDIT).map_err(wire)?;
f.finish().map_err(wire)?;
if !Grants::allows(&self.grants(c).register, &name) {
return err(ErrorCode::NotAuthorized, format!("may not register {name}"));
}
if max_queued > self.limits.max_service_queued
|| max_in_flight > self.limits.max_service_in_flight
{
return err(
ErrorCode::QuotaExceeded,
"maxQueued or maxInFlight exceeds the configured limit",
);
}
if self.services.contains_key(&name) {
return err(ErrorCode::Conflict, format!("{name} is already registered"));
}
if self.services.len() >= self.limits.max_services {
return err(ErrorCode::QuotaExceeded, "too many services");
}
self.next_service += 1;
let incarnation = serial_id("svc", self.next_service);
self.services.insert(
name.clone(),
Svc {
incarnation: incarnation.clone(),
conn: c,
max_queued,
max_in_flight,
in_flight: 0,
queue: VecDeque::new(),
},
);
self.conns.get_mut(&c).expect("present").services.push(name);
Ok(obj(vec![("serviceIncarnation", incarnation.into())]))
}
fn op_unregister(
&mut self,
c: ConnKey,
env: &Envelope,
) -> Result<Map<String, Value>, BusError> {
let mut f = Fields::of(&env.body, "service.unregister");
let name = f.name("name").map_err(wire)?;
let incarnation = f.id("serviceIncarnation").map_err(wire)?;
f.finish().map_err(wire)?;
let mine = self
.services
.get(&name)
.is_some_and(|s| s.conn == c && s.incarnation == incarnation);
if mine {
self.remove_service(&name, "unregistered");
}
Ok(obj(vec![("removed", mine.into())]))
}
/// Removes a registration. Queued calls fail with `call.failed`; each affected caller also
/// hears `route.removed`. Dispatched calls stay correlated: the service may still reply.
fn remove_service(&mut self, name: &str, reason: &str) {
let Some(svc) = self.services.remove(name) else {
return;
};
if let Some(conn) = self.conns.get_mut(&svc.conn) {
conn.services.retain(|n| n != name);
}
let mut callers = Vec::new();
for (_, call) in self.calls.iter() {
let open = matches!(call.phase, Phase::Queued | Phase::Dispatched { .. });
if call.service_incarnation == svc.incarnation
&& open
&& !call.detached
&& !callers.contains(&call.caller)
{
callers.push(call.caller);
}
}
for caller in callers {
let body = obj(vec![
("name", name.into()),
("serviceIncarnation", svc.incarnation.clone().into()),
("reason", reason.into()),
]);
self.notice(caller, "route.removed", body);
}
for key in svc.queue {
let Some(call) = self.calls.get(&key) else {
continue;
};
let body = obj(vec![
("callId", call.call_id.clone().into()),
("code", ErrorCode::NoService.as_str().into()),
(
"message",
format!("{name} was {reason} before the call was dispatched").into(),
),
("dispatch", Dispatch::NotDispatched.as_str().into()),
]);
let caller = call.caller;
self.notice(caller, "call.failed", body);
self.remove_call(key);
}
let abandoned: Vec<CallKey> = self
.calls
.iter()
.filter(|(_, call)| {
call.service_incarnation == svc.incarnation
&& matches!(call.phase, Phase::Dispatched { .. })
&& !call.reply_capability
})
.map(|(key, _)| *key)
.collect();
for key in abandoned {
let call = &self.calls[&key];
if !call.detached {
self.notice(
call.caller,
"call.failed",
obj(vec![
("callId", call.call_id.clone().into()),
("code", ErrorCode::NoService.as_str().into()),
(
"message",
format!("{name} was {reason} after dispatch").into(),
),
("dispatch", Dispatch::Dispatched.as_str().into()),
]),
);
}
self.remove_call(key);
}
}
/// Forgets a call: frees the caller's slot, the reply correlation and any roots the call
/// itself still holds (queued request, queued result).
fn remove_call(&mut self, key: CallKey) {
let Some(call) = self.calls.remove(&key) else {
return;
};
if let Some(conn) = self.conns.get_mut(&call.caller) {
if conn.active_calls.get(&call.call_serial) == Some(&key) {
conn.active_calls.remove(&call.call_serial);
}
conn.result_queue.retain(|k| *k != key);
}
match call.phase {
Phase::Queued => self.drop_roots(&call.artifacts),
Phase::Dispatched { delivery } => {
if let Some(conn) = self.conns.get_mut(&call.service_conn) {
conn.requests.remove(&delivery);
if call.reply_capability {
conn.reply_capabilities -= 1;
}
}
}
Phase::Replied { delivery, result } => {
if let Some(conn) = self.conns.get_mut(&call.service_conn) {
conn.requests.remove(&delivery);
if call.reply_capability {
conn.reply_capabilities -= 1;
}
}
if let CallResult::Queued(msg) = result {
self.drop_roots(&msg.artifacts);
}
}
}
}
fn op_call(&mut self, c: ConnKey, env: &Envelope) -> Result<Map<String, Value>, BusError> {
let mut f = Fields::of(&env.body, "rpc.call");
let call_id = f.id("callId").map_err(wire)?;
let target = f.name("target").map_err(wire)?;
let expected = f.nullable_id("expectedIncarnation").map_err(wire)?;
let method = f.method("method").map_err(wire)?;
let payload = f.object("payload").map_err(wire)?.clone();
f.finish().map_err(wire)?;
let Some(call_serial) = parse_serial_id("call", &call_id) else {
return err(ErrorCode::InvalidEnvelope, "callId must be call-<U64>");
};
if self.conns[&c]
.call_watermark
.is_some_and(|w| call_serial <= w)
{
return err(
ErrorCode::InvalidEnvelope,
"callId was already used or is not increasing",
);
}
// The issued-id history advances after syntactic validation, before semantic admission.
self.conns.get_mut(&c).expect("present").call_watermark = Some(call_serial);
if !Grants::allows(&self.grants(c).call, &target) {
return err(ErrorCode::NotAuthorized, format!("may not call {target}"));
}
let conn = &self.conns[&c];
if conn.active_calls.len() >= self.limits.max_active_calls_per_client {
return err(ErrorCode::Backpressure, "too many active calls");
}
let Some(svc) = self.services.get(&target) else {
return err(ErrorCode::NoService, format!("{target} is not registered"));
};
if expected.as_ref().is_some_and(|e| *e != svc.incarnation) {
return err(
ErrorCode::TargetChanged,
format!("{target} is now {}", svc.incarnation),
);
}
if svc.queue.len() as u64 >= svc.max_queued {
return err(ErrorCode::Backpressure, format!("{target} queue is full"));
}
let artifacts = self.check_attachments(c, &env.attachments)?;
let call = Call {
caller: c,
caller_identity: self.identity(c),
call_serial,
call_id,
target: target.clone(),
service_incarnation: svc.incarnation.clone(),
service_conn: svc.conn,
method,
payload,
attachments: env
.attachments
.iter()
.map(|a| (a.name.clone(), a.reference.clone()))
.collect(),
artifacts,
phase: Phase::Queued,
detached: false,
reply_capability: false,
};
let placeholder = serial_id("dlv", MAX_SERIAL);
let size = frame_len(
Kind::Delivery,
"rpc.request",
call.request_body(&placeholder),
attachments_with_owner(&call.attachments, &placeholder),
);
if size > MAX_ENVELOPE_BYTES {
return err(
ErrorCode::InvalidEnvelope,
"the request delivery would exceed the envelope limit",
);
}
// Admission: roots first, then the queue entry, then the reply.
self.add_roots(&call.artifacts);
self.next_call += 1;
let key = self.next_call;
let incarnation = call.service_incarnation.clone();
let service_conn = call.service_conn;
self.calls.insert(key, call);
self.services
.get_mut(&target)
.expect("checked")
.queue
.push_back(key);
let conn = self.conns.get_mut(&c).expect("present");
conn.active_calls.insert(call_serial, key);
self.wake(service_conn);
Ok(obj(vec![
("accepted", true.into()),
("serviceIncarnation", incarnation.into()),
]))
}
fn op_reply(&mut self, c: ConnKey, env: &Envelope) -> Result<Map<String, Value>, BusError> {
let mut f = Fields::of(&env.body, "rpc.reply");
let call_id = f.id("callId").map_err(wire)?;
let delivery_id = f.id("requestDeliveryId").map_err(wire)?;
let outcome = f.object("outcome").map_err(wire)?.clone();
f.finish().map_err(wire)?;
let Some(n) = parse_serial_id("dlv", &delivery_id) else {
return err(
ErrorCode::InvalidEnvelope,
"requestDeliveryId must be dlv-<U64>",
);
};
let conn = &self.conns[&c];
if n > conn.delivery_issued {
return err(
ErrorCode::OwnerInvalid,
"requestDeliveryId was never issued to this connection",
);
}
let Some(&key) = conn.requests.get(&n) else {
return err(
ErrorCode::CallGone,
"no call awaits a reply for that request",
);
};
let call = &self.calls[&key];
if call.call_id != call_id {
return err(
ErrorCode::InvalidEnvelope,
"callId does not match the request",
);
}
if matches!(call.phase, Phase::Replied { .. }) {
return err(ErrorCode::CallGone, "the call was already replied to");
}
let artifacts = self.check_attachments(c, &env.attachments)?;
let result = ResultMsg {
responder: self.identity(c),
outcome,
attachments: env
.attachments
.iter()
.map(|a| (a.name.clone(), a.reference.clone()))
.collect(),
artifacts,
};
let placeholder = serial_id("dlv", MAX_SERIAL);
let size = frame_len(
Kind::Delivery,
"rpc.result",
call.result_body(&placeholder, &result),
attachments_with_owner(&result.attachments, &placeholder),
);
if size > MAX_ENVELOPE_BYTES {
return err(
ErrorCode::InvalidEnvelope,
"the result delivery would exceed the envelope limit",
);
}
if call.detached {
self.remove_call(key);
return Ok(obj(vec![("routed", false.into())]));
}
let caller = call.caller;
self.add_roots(&result.artifacts);
let released_capability = self.calls[&key].reply_capability;
if released_capability {
self.conns
.get_mut(&c)
.expect("service is connected")
.reply_capabilities -= 1;
}
let call = self.calls.get_mut(&key).expect("present");
call.reply_capability = false;
call.phase = Phase::Replied {
delivery: n,
result: CallResult::Queued(result),
};
self.conns
.get_mut(&caller)
.expect("attached caller is connected")
.result_queue
.push_back(key);
self.wake(caller);
Ok(obj(vec![("routed", true.into())]))
}
fn op_responder_release(
&mut self,
c: ConnKey,
env: &Envelope,
) -> Result<Map<String, Value>, BusError> {
let mut f = Fields::of(&env.body, "rpc.responder.release");
let call_id = f.id("callId").map_err(wire)?;
let delivery_id = f.id("requestDeliveryId").map_err(wire)?;
f.finish().map_err(wire)?;
let Some(delivery) = parse_serial_id("dlv", &delivery_id) else {
return err(
ErrorCode::InvalidEnvelope,
"requestDeliveryId must be dlv-<U64>",
);
};
if delivery > self.conns[&c].delivery_issued {
return err(
ErrorCode::OwnerInvalid,
"requestDeliveryId was never issued to this connection",
);
}
let Some(&key) = self.conns[&c].requests.get(&delivery) else {
return Ok(obj(vec![("released", false.into())]));
};
if self.calls[&key].call_id != call_id {
return err(
ErrorCode::InvalidEnvelope,
"callId does not match the request",
);
}
if !self.calls[&key].reply_capability {
return Ok(obj(vec![("released", false.into())]));
}
let detached = self.calls[&key].detached;
let service_live = {
let call = &self.calls[&key];
self.services.get(&call.target).is_some_and(|service| {
service.conn == call.service_conn && service.incarnation == call.service_incarnation
})
};
self.calls
.get_mut(&key)
.expect("request maps to a call")
.reply_capability = false;
self.conns
.get_mut(&c)
.expect("service is connected")
.reply_capabilities -= 1;
if detached {
self.remove_call(key);
} else {
let call = &self.calls[&key];
let (code, message) = if service_live {
(
ErrorCode::CallGone,
"the last responder was released without replying",
)
} else {
(
ErrorCode::NoService,
"the service route ended after dispatch without a reply",
)
};
self.notice(
call.caller,
"call.failed",
obj(vec![
("callId", call.call_id.clone().into()),
("code", code.as_str().into()),
("message", message.into()),
("dispatch", Dispatch::Dispatched.as_str().into()),
]),
);
self.remove_call(key);
}
Ok(obj(vec![("released", true.into())]))
}
fn op_cancel(&mut self, c: ConnKey, env: &Envelope) -> Result<Map<String, Value>, BusError> {
let mut f = Fields::of(&env.body, "rpc.cancel");
let call_id = f.id("callId").map_err(wire)?;
f.finish().map_err(wire)?;
let Some(serial) = parse_serial_id("call", &call_id) else {
return err(ErrorCode::InvalidEnvelope, "callId must be call-<U64>");
};
let state = match self.conns[&c].active_calls.get(&serial).copied() {
None => "call-gone",
Some(key) => match self.calls[&key].phase {
Phase::Queued => {
let call = &self.calls[&key];
let (target, inc) = (call.target.clone(), call.service_incarnation.clone());
if let Some(svc) = self
.services
.get_mut(&target)
.filter(|s| s.incarnation == inc)
{
svc.queue.retain(|k| *k != key);
}
self.remove_call(key);
"cancelled-before-dispatch"
}
Phase::Dispatched { .. } => {
self.calls.get_mut(&key).expect("present").detached = true;
self.conns
.get_mut(&c)
.expect("present")
.active_calls
.remove(&serial);
if !self.calls[&key].reply_capability {
self.remove_call(key);
}
"execution-unknown"
}
Phase::Replied { .. } => "completed",
},
};
Ok(obj(vec![("state", state.into())]))
}
// ---- topics
fn op_declare(&mut self, c: ConnKey, env: &Envelope) -> Result<Map<String, Value>, BusError> {
let mut f = Fields::of(&env.body, "topic.declare");
let name = f.name("name").map_err(wire)?;
let retain = match f.string("retained").map_err(wire)? {
"none" => false,
"latest" => true,
_ => {
return err(
ErrorCode::InvalidEnvelope,
"retained is \"none\" or \"latest\"",
);
}
};
f.finish().map_err(wire)?;
if !Grants::allows(&self.grants(c).manage_topics, &name) {
return err(ErrorCode::NotAuthorized, format!("may not declare {name}"));
}
if let Some(t) = self.topics.get(&name) {
if t.retain != retain {
return err(
ErrorCode::Conflict,
format!("{name} is declared with other settings"),
);
}
return Ok(obj(vec![
("declared", false.into()),
("topicIncarnation", t.incarnation.clone().into()),
]));
}
if self.topics.len() >= self.limits.max_topics {
return err(ErrorCode::QuotaExceeded, "too many topics");
}
self.next_topic += 1;
let incarnation = serial_id("top", self.next_topic);
self.topics.insert(
name,
Topic {
incarnation: incarnation.clone(),
retain,
sequence: 0,
retained: None,
subscribers: Vec::new(),
},
);
Ok(obj(vec![
("declared", true.into()),
("topicIncarnation", incarnation.into()),
]))
}
fn op_clear(&mut self, c: ConnKey, env: &Envelope) -> Result<Map<String, Value>, BusError> {
let mut f = Fields::of(&env.body, "topic.clear");
let name = f.name("name").map_err(wire)?;
f.finish().map_err(wire)?;
if !Grants::allows(&self.grants(c).manage_topics, &name) {
return err(ErrorCode::NotAuthorized, format!("may not clear {name}"));
}
let Some(t) = self.topics.get_mut(&name) else {
return err(ErrorCode::NoTopic, format!("{name} is not declared"));
};
let old = t.retained.take();
let cleared = old.is_some();
if let Some(msg) = old {
self.retained_bytes -= msg.artifact_bytes;
self.drop_roots(&msg.artifacts);
}
Ok(obj(vec![("cleared", cleared.into())]))
}
fn op_delete(&mut self, c: ConnKey, env: &Envelope) -> Result<Map<String, Value>, BusError> {
let mut f = Fields::of(&env.body, "topic.delete");
let name = f.name("name").map_err(wire)?;
f.finish().map_err(wire)?;
if !Grants::allows(&self.grants(c).manage_topics, &name) {
return err(ErrorCode::NotAuthorized, format!("may not delete {name}"));
}
let Some(t) = self.topics.get(&name) else {
return Ok(obj(vec![("deleted", false.into())]));
};
if !t.subscribers.is_empty() {
return err(ErrorCode::Conflict, format!("{name} still has subscribers"));
}
let t = self.topics.remove(&name).expect("present");
if let Some(msg) = t.retained {
self.retained_bytes -= msg.artifact_bytes;
self.drop_roots(&msg.artifacts);
}
Ok(obj(vec![("deleted", true.into())]))
}
fn op_subscribe(&mut self, c: ConnKey, env: &Envelope) -> Result<Map<String, Value>, BusError> {
let mut f = Fields::of(&env.body, "subscribe");
let topic = f.name("topic").map_err(wire)?;
let latest = match f.string("mode").map_err(wire)? {
"latest" => true,
"bounded" => false,
_ => {
return err(
ErrorCode::InvalidEnvelope,
"mode is \"latest\" or \"bounded\"",
);
}
};
let max_queued = f.int("maxQueued", 1, MAX_CREDIT).map_err(wire)?;
let max_in_flight = f.int("maxInFlight", 1, MAX_CREDIT).map_err(wire)?;
let replay = f.boolean("replayLatest").map_err(wire)?;
f.finish().map_err(wire)?;
if !Grants::allows(&self.grants(c).subscribe, &topic) {
return err(
ErrorCode::NotAuthorized,
format!("may not subscribe to {topic}"),
);
}
if latest && max_queued != 1 {
return err(
ErrorCode::InvalidEnvelope,
"latest subscriptions have maxQueued 1",
);
}
let (queue_cap, credit_cap) = if latest {
(1, self.limits.max_latest_in_flight)
} else {
(
self.limits.max_bounded_queued,
self.limits.max_bounded_in_flight,
)
};
if max_queued > queue_cap || max_in_flight > credit_cap {
return err(
ErrorCode::QuotaExceeded,
"maxQueued or maxInFlight exceeds the configured limit",
);
}
let Some(t) = self.topics.get(&topic) else {
return err(ErrorCode::NoTopic, format!("{topic} is not declared"));
};
let conn = &self.conns[&c];
if conn.subs.len() >= self.limits.max_subscriptions_per_client
|| self.subscriptions >= self.limits.max_subscriptions
{
return err(ErrorCode::QuotaExceeded, "too many subscriptions");
}
let incarnation = t.incarnation.clone();
let replayed = if replay { t.retained.clone() } else { None };
if !latest
&& replayed.as_ref().is_some_and(|msg| {
conn.queued_bytes.saturating_add(msg.bytes)
> self.limits.max_queued_bytes_per_client
})
{
return err(
ErrorCode::Backpressure,
"retained replay exceeds the bounded subscriber's queued-byte limit",
);
}
let mut sub = Sub {
topic: topic.clone(),
topic_incarnation: incarnation.clone(),
latest,
max_queued,
max_in_flight,
in_flight: 0,
queue: VecDeque::new(),
replaced: 0,
};
let mut bytes = 0;
if let Some(msg) = replayed {
self.add_roots(&msg.artifacts);
if !latest {
bytes = msg.bytes;
}
sub.queue.push_back(msg);
}
let conn = self.conns.get_mut(&c).expect("present");
conn.sub_issued += 1;
let id = conn.sub_issued;
conn.queued_bytes += bytes;
conn.subs.insert(id, sub);
conn.sub_order.push(id);
self.subscriptions += 1;
self.topics
.get_mut(&topic)
.expect("present")
.subscribers
.push((c, id));
self.wake(c);
Ok(obj(vec![
("subscriptionId", serial_id("sub", id).into()),
("topicIncarnation", incarnation.into()),
]))
}
/// Removes a subscription and releases its queue. Deliveries already handed out keep their
/// roots until consumed.
fn drop_subscription(&mut self, c: ConnKey, id: u64) -> bool {
let Some(conn) = self.conns.get_mut(&c) else {
return false;
};
let Some(sub) = conn.subs.remove(&id) else {
return false;
};
conn.sub_order.retain(|s| *s != id);
self.subscriptions -= 1;
if !sub.latest {
conn.queued_bytes -= sub.queue.iter().map(|m| m.bytes).sum::<usize>();
}
if let Some(t) = self
.topics
.get_mut(&sub.topic)
.filter(|t| t.incarnation == sub.topic_incarnation)
{
t.subscribers.retain(|s| *s != (c, id));
}
for msg in sub.queue {
self.drop_roots(&msg.artifacts);
}
true
}
fn op_unsubscribe(
&mut self,
c: ConnKey,
env: &Envelope,
) -> Result<Map<String, Value>, BusError> {
let mut f = Fields::of(&env.body, "unsubscribe");
let sid = f.id("subscriptionId").map_err(wire)?;
f.finish().map_err(wire)?;
let removed = parse_serial_id("sub", &sid).is_some_and(|id| self.drop_subscription(c, id));
Ok(obj(vec![("removed", removed.into())]))
}
fn op_publish(&mut self, c: ConnKey, env: &Envelope) -> Result<Map<String, Value>, BusError> {
let mut f = Fields::of(&env.body, "publish");
let topic = f.name("topic").map_err(wire)?;
let payload = f.object("payload").map_err(wire)?.clone();
f.finish().map_err(wire)?;
if !Grants::allows(&self.grants(c).publish, &topic) {
return err(
ErrorCode::NotAuthorized,
format!("may not publish to {topic}"),
);
}
let Some(t) = self.topics.get(&topic) else {
return err(ErrorCode::NoTopic, format!("{topic} is not declared"));
};
let artifacts = self.check_attachments(c, &env.attachments)?;
let artifact_bytes: u64 = artifacts
.iter()
.map(|s| self.artifacts[s].byte_length)
.sum();
let mut msg = TopicMsg {
topic: topic.clone(),
topic_incarnation: t.incarnation.clone(),
sequence: t.sequence + 1,
payload,
attachments: env
.attachments
.iter()
.map(|a| (a.name.clone(), a.reference.clone()))
.collect(),
artifacts,
bytes: 0,
artifact_bytes,
};
let placeholder = serial_id("dlv", MAX_SERIAL);
msg.bytes = frame_len(
Kind::Delivery,
"topic.message",
msg.body(&placeholder, &serial_id("sub", MAX_SERIAL), MAX_SERIAL),
attachments_with_owner(&msg.attachments, &placeholder),
);
if msg.bytes > MAX_ENVELOPE_BYTES {
return err(
ErrorCode::InvalidEnvelope,
"the delivery would exceed the envelope limit",
);
}
// Admission is all or nothing: check every subscriber and the retention quota before
// touching anything.
let mut extra: HashMap<ConnKey, usize> = HashMap::new();
for (sc, sid) in &t.subscribers {
let conn = &self.conns[sc];
let sub = &conn.subs[sid];
if sub.latest {
continue;
}
if sub.queue.len() as u64 >= sub.max_queued {
return err(
ErrorCode::Backpressure,
"a bounded subscriber's queue is full",
);
}
let add = extra.entry(*sc).or_default();
*add += msg.bytes;
if conn.queued_bytes + *add > self.limits.max_queued_bytes_per_client {
return err(
ErrorCode::Backpressure,
"a bounded subscriber's queued bytes are at the limit",
);
}
}
if t.retain {
let old = t.retained.as_ref().map_or(0, |m| m.artifact_bytes);
if self.retained_bytes - old + msg.artifact_bytes > self.limits.max_retained_bytes {
return err(ErrorCode::QuotaExceeded, "retained bytes are at the limit");
}
}
// Accept.
let msg = Arc::new(msg);
let t = self.topics.get_mut(&topic).expect("present");
t.sequence += 1;
let subscribers = t.subscribers.clone();
let retain = t.retain;
let mut replaced = 0u64;
for (sc, sid) in &subscribers {
let conn = self.conns.get_mut(sc).expect("subscriber is connected");
let sub = conn.subs.get_mut(sid).expect("subscription is live");
let dropped = if sub.latest {
let old = sub.queue.pop_front();
if old.is_some() {
sub.replaced += 1;
replaced += 1;
}
old
} else {
conn.queued_bytes += msg.bytes;
None
};
sub.queue.push_back(msg.clone());
self.add_roots(&msg.artifacts);
if let Some(old) = dropped {
self.drop_roots(&old.artifacts);
}
self.wake(*sc);
}
if retain {
self.add_roots(&msg.artifacts);
self.retained_bytes += msg.artifact_bytes;
let old = self
.topics
.get_mut(&topic)
.expect("present")
.retained
.replace(msg.clone());
if let Some(old) = old {
self.retained_bytes -= old.artifact_bytes;
self.drop_roots(&old.artifacts);
}
}
Ok(obj(vec![
("topicSequence", msg.sequence.to_string().into()),
("subscribers", subscribers.len().to_string().into()),
("replaced", replaced.to_string().into()),
]))
}
// ---- ownership
fn op_consumed(&mut self, c: ConnKey, env: &Envelope) -> Result<Map<String, Value>, BusError> {
let mut f = Fields::of(&env.body, "delivery.consumed");
let ids = id_batch(
f.array("deliveryIds", 1, MAX_BATCH).map_err(wire)?,
"deliveryIds",
)
.map_err(wire)?;
f.finish().map_err(wire)?;
let issued = self.conns[&c].delivery_issued;
let mut serials = Vec::with_capacity(ids.len());
for id in &ids {
match parse_serial_id("dlv", id) {
Some(n) if n <= issued => serials.push(n),
Some(_) => return err(ErrorCode::OwnerInvalid, format!("{id} was never issued")),
None => {
return err(
ErrorCode::OwnerInvalid,
format!("{id} is not a delivery id"),
);
}
}
}
let mut released = 0u64;
for n in serials {
if let Some(owner) = self.take_owner(c, OwnerKey::Delivery(n)) {
released += 1;
self.release_owner(c, owner);
}
}
Ok(obj(vec![("released", released.to_string().into())]))
}
fn op_release(&mut self, c: ConnKey, env: &Envelope) -> Result<Map<String, Value>, BusError> {
let mut f = Fields::of(&env.body, "artifact.release");
let ids =
id_batch(f.array("ownerIds", 1, MAX_BATCH).map_err(wire)?, "ownerIds").map_err(wire)?;
f.finish().map_err(wire)?;
let issued = self.conns[&c].hold_issued;
let mut serials = Vec::with_capacity(ids.len());
for id in &ids {
match parse_serial_id("own", id) {
Some(n) if n <= issued => serials.push(n),
Some(_) => return err(ErrorCode::OwnerInvalid, format!("{id} was never issued")),
None => {
return err(
ErrorCode::OwnerInvalid,
format!("{id} is not a hold; deliveries end with delivery.consumed"),
);
}
}
}
let mut released = 0u64;
for n in serials {
if let Some(owner) = self.take_owner(c, OwnerKey::Hold(n)) {
released += 1;
self.release_owner(c, owner);
}
}
Ok(obj(vec![("released", released.to_string().into())]))
}
fn op_open(&mut self, c: ConnKey, env: &Envelope) -> Result<Map<String, Value>, BusError> {
let mut f = Fields::of(&env.body, "artifact.open");
let r = ArtifactRef::from_json(f.value("ref").map_err(wire)?).map_err(wire)?;
let owner = f.id("ownerId").map_err(wire)?;
f.finish().map_err(wire)?;
let serial = self.check_owned(c, &r, &owner)?;
let loc = crate::wire::Location {
store_id: self.store_id.clone(),
relative_path: sealed_rel(serial),
};
Ok(obj(vec![("readLocation", loc.to_json())]))
}
fn op_retain(&mut self, c: ConnKey, env: &Envelope) -> Result<Map<String, Value>, BusError> {
let mut f = Fields::of(&env.body, "artifact.retain");
let r = ArtifactRef::from_json(f.value("ref").map_err(wire)?).map_err(wire)?;
let owner = f.id("ownerId").map_err(wire)?;
f.finish().map_err(wire)?;
let serial = self.check_owned(c, &r, &owner)?;
if !self.ordinary_budget_left(c) {
return err(ErrorCode::QuotaExceeded, "owner budget exhausted");
}
let conn = self.conns.get_mut(&c).expect("present");
conn.hold_issued += 1;
let n = conn.hold_issued;
self.insert_owner(c, OwnerKey::Hold(n), Owner::Hold { artifact: serial });
self.add_roots(&[serial]);
Ok(obj(vec![("ownerId", serial_id("own", n).into())]))
}
fn op_allocate(&mut self, c: ConnKey, env: &Envelope) -> Result<AllocJob, BusError> {
let mut f = Fields::of(&env.body, "artifact.allocate");
let len = f.u64_string("byteLength").map_err(wire)?;
let content_type = f.string("contentType").map_err(wire)?.to_owned();
f.finish().map_err(wire)?;
if !crate::wire::is_content_type(&content_type) {
return err(
ErrorCode::InvalidEnvelope,
"contentType must be 1..=127 printable ASCII characters",
);
}
if len > self.limits.max_artifact_bytes {
return err(
ErrorCode::QuotaExceeded,
"byteLength exceeds the per-object limit",
);
}
if self.store_bytes.saturating_add(len) > self.limits.max_store_bytes {
return err(ErrorCode::QuotaExceeded, "the store is full");
}
if !self.ordinary_budget_left(c) {
return err(ErrorCode::QuotaExceeded, "owner budget exhausted");
}
self.next_artifact += 1;
let serial = self.next_artifact;
self.artifacts.insert(
serial,
Art {
state: ArtState::Writing,
byte_length: len,
content_type,
digest: None,
roots: 0,
},
);
self.store_bytes += len;
let conn = self.conns.get_mut(&c).expect("present");
conn.hold_issued += 1;
let owner = conn.hold_issued;
self.insert_owner(
c,
OwnerKey::Hold(owner),
Owner::Writer {
artifact: serial,
sealing: false,
},
);
Ok(AllocJob {
conn: c,
reply_to: env.id.clone(),
serial,
len,
owner,
})
}
pub(crate) fn finish_allocate(&mut self, job: AllocJob, created: std::io::Result<()>) {
let live = self.conns.get(&job.conn).is_some_and(|conn| {
matches!(
conn.owners.get(&OwnerKey::Hold(job.owner)),
Some(Owner::Writer { .. })
)
});
match created {
Err(e) => {
if live {
self.take_owner(job.conn, OwnerKey::Hold(job.owner));
}
if let Some(a) = self.artifacts.remove(&job.serial) {
self.store_bytes -= a.byte_length;
}
self.reply(
job.conn,
&job.reply_to,
"artifact.allocate",
err(ErrorCode::StoreFailure, format!("staging: {e}")),
);
}
Ok(()) if !live || !self.artifacts.contains_key(&job.serial) => {
// The connection went away while the file was being made.
self.unlinks.push(staging_rel(job.serial));
}
Ok(()) => {
let loc = crate::wire::Location {
store_id: self.store_id.clone(),
relative_path: staging_rel(job.serial),
};
let value = obj(vec![
("artifactId", serial_id("a", job.serial).into()),
("generation", GENERATION.to_string().into()),
("ownerId", serial_id("own", job.owner).into()),
("writeLocation", loc.to_json()),
]);
self.reply(job.conn, &job.reply_to, "artifact.allocate", Ok(value));
}
}
self.flush_notices();
}
fn op_seal(&mut self, c: ConnKey, env: &Envelope) -> Result<SealJob, BusError> {
let mut f = Fields::of(&env.body, "artifact.seal");
let artifact_id = f.id("artifactId").map_err(wire)?;
let generation = f.u64_string("generation").map_err(wire)?;
let owner_id = f.id("ownerId").map_err(wire)?;
let digest = match f.value("digest").map_err(wire)? {
Value::Null => None,
Value::String(s) if crate::wire::is_digest(s) => Some(s.clone()),
_ => {
return err(
ErrorCode::InvalidEnvelope,
"digest must be null or 64 lowercase hex digits",
);
}
};
f.finish().map_err(wire)?;
let Some(serial) = parse_serial_id("a", &artifact_id) else {
return err(ErrorCode::ArtifactGone, "unknown artifact id");
};
let Some(owner) = parse_serial_id("own", &owner_id) else {
return err(ErrorCode::OwnerInvalid, "ownerId is not a writer");
};
match self.conns[&c].owners.get(&OwnerKey::Hold(owner)) {
Some(Owner::Writer {
artifact,
sealing: false,
}) if *artifact == serial => {}
Some(Owner::Writer {
artifact,
sealing: true,
}) if *artifact == serial => {
return err(ErrorCode::OwnerInvalid, "a seal is already in progress");
}
Some(Owner::Hold { artifact }) if *artifact == serial => {
return err(ErrorCode::OwnerInvalid, "the artifact is already sealed");
}
_ => {
return err(
ErrorCode::OwnerInvalid,
format!("{owner_id} is not the writer of {artifact_id}"),
);
}
}
if generation != GENERATION {
return err(ErrorCode::ArtifactGone, "unknown generation");
}
let len = self.artifacts[&serial].byte_length;
if self.store_bytes.saturating_add(len) > self.limits.max_store_bytes {
self.take_owner(c, OwnerKey::Hold(owner));
self.abandon_writer(serial);
return err(
ErrorCode::QuotaExceeded,
"no room for the sealing copy; the staging object was released",
);
}
self.store_bytes += len;
self.artifacts.get_mut(&serial).expect("present").state = ArtState::Sealing;
let conn = self.conns.get_mut(&c).expect("present");
conn.owners.insert(
OwnerKey::Hold(owner),
Owner::Writer {
artifact: serial,
sealing: true,
},
);
Ok(SealJob {
conn: c,
reply_to: env.id.clone(),
serial,
len,
digest,
owner,
})
}
pub(crate) fn finish_seal(
&mut self,
job: SealJob,
result: Result<(), crate::store::SealFailure>,
) {
let owner_live = self.conns.get(&job.conn).is_some_and(|conn| {
matches!(conn.owners.get(&OwnerKey::Hold(job.owner)), Some(Owner::Writer { artifact, sealing: true }) if *artifact == job.serial)
});
let reply = match (result, owner_live) {
(Ok(()), true) => {
let art = self
.artifacts
.get_mut(&job.serial)
.expect("sealing artifact is registered");
art.state = ArtState::Sealed;
art.digest = job.digest.clone();
art.roots = 1;
// The seal task already unlinked staging; its reservation ends here.
self.store_bytes -= job.len;
let conn = self.conns.get_mut(&job.conn).expect("present");
conn.owners.insert(
OwnerKey::Hold(job.owner),
Owner::Hold {
artifact: job.serial,
},
);
let r = self.reference(job.serial).expect("present");
Ok(obj(vec![
("ref", r.to_json()),
("ownerId", serial_id("own", job.owner).into()),
]))
}
(result, _) => {
if self.artifacts.remove(&job.serial).is_some() {
self.store_bytes -= 2 * job.len;
}
self.unlinks.push(sealed_rel(job.serial));
if owner_live {
self.take_owner(job.conn, OwnerKey::Hold(job.owner));
}
match result {
Ok(()) => err(
ErrorCode::ArtifactGone,
"the writer was released during the seal",
),
Err(crate::store::SealFailure::Mismatch(m)) => {
err(ErrorCode::ArtifactMismatch, m)
}
Err(crate::store::SealFailure::Io(e)) => {
err(ErrorCode::StoreFailure, format!("seal: {e}"))
}
}
}
};
self.reply(job.conn, &job.reply_to, "artifact.seal", reply);
self.flush_notices();
}
// -----------------------------------------------------------------------------------------
// Writers pull frames
fn topic_ready(&self, c: ConnKey) -> bool {
if !self.ordinary_budget_left(c) {
return false;
}
let conn = &self.conns[&c];
conn.subs
.values()
.any(|s| s.in_flight < s.max_in_flight && !s.queue.is_empty())
}
/// The next frame for `c`: control first, then RPC, then topic data, with topic data
/// guaranteed a turn after [`TOPIC_STARVATION_LIMIT`] higher-priority frames.
pub(crate) fn next_frame(&mut self, c: ConnKey) -> NextFrame {
if !self.conns.contains_key(&c) {
return NextFrame::Gone;
}
let topic_ready = self.topic_ready(c);
let starving = topic_ready && self.conns[&c].streak >= TOPIC_STARVATION_LIMIT;
if !starving && let Some(f) = self.pop_control(c).or_else(|| self.dispatch_rpc(c)) {
if topic_ready {
self.conns.get_mut(&c).expect("present").streak += 1;
}
return self.selected_frame(c, f);
}
if let Some(f) = self.dispatch_topic(c) {
self.conns.get_mut(&c).expect("present").streak = 0;
return self.selected_frame(c, f);
}
match self.pop_control(c).or_else(|| self.dispatch_rpc(c)) {
Some(f) => self.selected_frame(c, f),
None => NextFrame::Idle,
}
}
fn selected_frame(&mut self, c: ConnKey, frame: Outbound) -> NextFrame {
match self.encode_outbound(c, frame) {
Some(bytes) => NextFrame::Frame(bytes),
None => NextFrame::Gone,
}
}
fn pop_control(&mut self, c: ConnKey) -> Option<Outbound> {
let conn = self.conns.get_mut(&c)?;
let f = conn.control.pop_front()?;
conn.control_bytes -= f.bytes;
Some(f)
}
fn dispatch_rpc(&mut self, c: ConnKey) -> Option<Outbound> {
if self.conns[&c].owners.len() + self.conns[&c].reply_capabilities
>= self.limits.max_owners_per_client
{
return None;
}
// Results for calls this connection made.
if let Some(key) = self.conns.get_mut(&c)?.result_queue.pop_front() {
let call = self.calls.get_mut(&key)?;
let Phase::Replied { delivery, result } =
std::mem::replace(&mut call.phase, Phase::Queued)
else {
unreachable!("queued results belong to replied calls");
};
let CallResult::Queued(msg) = result else {
unreachable!("result queued once")
};
call.phase = Phase::Replied {
delivery,
result: CallResult::Delivered,
};
let conn = self.conns.get_mut(&c).expect("present");
conn.delivery_issued += 1;
let n = conn.delivery_issued;
let did = serial_id("dlv", n);
let call = &self.calls[&key];
let body = call.result_body(&did, &msg);
let atts = attachments_with_owner(&msg.attachments, &did);
self.insert_owner(
c,
OwnerKey::Delivery(n),
Owner::Delivery {
artifacts: msg.artifacts,
kind: DeliveryKind::Result { call: key },
},
);
return self.outbound(Kind::Delivery, "rpc.result", None, body, atts);
}
// Requests for services this connection registered, round robin.
let conn = &self.conns[&c];
let n_svcs = conn.services.len();
for i in 0..n_svcs {
let idx = (conn.svc_cursor + i) % n_svcs;
let name = conn.services[idx].clone();
let svc = self.services.get_mut(&name)?;
if svc.in_flight >= svc.max_in_flight || svc.queue.is_empty() {
continue;
}
if self.conns[&c].owners.len() + self.conns[&c].reply_capabilities + 2
> self.limits.max_owners_per_client
{
continue;
}
let key = svc.queue.pop_front().expect("nonempty");
svc.in_flight += 1;
let incarnation = svc.incarnation.clone();
let conn = self.conns.get_mut(&c).expect("present");
conn.svc_cursor = idx + 1;
conn.delivery_issued += 1;
let n = conn.delivery_issued;
conn.requests.insert(n, key);
conn.reply_capabilities += 1;
let call = self.calls.get_mut(&key).expect("queued call is registered");
// Dispatched before a byte of it is written.
call.phase = Phase::Dispatched { delivery: n };
call.reply_capability = true;
let did = serial_id("dlv", n);
let body = call.request_body(&did);
let atts = attachments_with_owner(&call.attachments, &did);
let artifacts = call.artifacts.clone();
self.insert_owner(
c,
OwnerKey::Delivery(n),
Owner::Delivery {
artifacts,
kind: DeliveryKind::Request {
call: key,
service: name,
incarnation,
},
},
);
return self.outbound(Kind::Delivery, "rpc.request", None, body, atts);
}
None
}
fn dispatch_topic(&mut self, c: ConnKey) -> Option<Outbound> {
if !self.ordinary_budget_left(c) {
return None;
}
let conn = self.conns.get_mut(&c)?;
let n_subs = conn.sub_order.len();
for i in 0..n_subs {
let idx = (conn.sub_cursor + i) % n_subs;
let id = conn.sub_order[idx];
let sub = conn.subs.get_mut(&id).expect("ordered subscription exists");
if sub.in_flight >= sub.max_in_flight || sub.queue.is_empty() {
continue;
}
let msg = sub.queue.pop_front().expect("nonempty");
sub.in_flight += 1;
let replaced = std::mem::take(&mut sub.replaced);
if !sub.latest {
conn.queued_bytes -= msg.bytes;
}
conn.sub_cursor = idx + 1;
conn.delivery_issued += 1;
let n = conn.delivery_issued;
let did = serial_id("dlv", n);
let body = msg.body(&did, &serial_id("sub", id), replaced);
let atts = attachments_with_owner(&msg.attachments, &did);
self.insert_owner(
c,
OwnerKey::Delivery(n),
Owner::Delivery {
artifacts: msg.artifacts.clone(),
kind: DeliveryKind::Topic { sub: id },
},
);
return self.outbound(Kind::Delivery, "topic.message", None, body, atts);
}
None
}
}
fn reply_body(result: Result<Map<String, Value>, BusError>) -> Map<String, Value> {
match result {
Ok(value) => obj(vec![("ok", true.into()), ("value", Value::Object(value))]),
Err(e) => obj(vec![
("ok", false.into()),
(
"error",
Value::Object(obj(vec![
("code", e.code.as_str().into()),
("message", e.message.into()),
("dispatch", e.dispatch.as_str().into()),
])),
),
]),
}
}