liminal/channel/observer.rs
1//! SRV-005: the cluster observer seam.
2//!
3//! Clustering lives in `liminal-server`, but it must learn about three channel
4//! events that only the library can see at first hand: a subscriber joining, a
5//! subscriber leaving, and a message being published. Rather than leak the
6//! cluster's distribution types into the library (which would violate the
7//! embedded-first boundary), the library exposes this thin observer trait. The
8//! server installs a single observer on the shared [`ChannelSupervisor`]; the
9//! [`ChannelHandle`](crate::channel::ChannelHandle) calls it host-side after the
10//! corresponding channel-actor operation succeeds.
11//!
12//! The library itself never implements this trait beyond a no-op default — it
13//! adds NO clustering behaviour. The observer is the entire contract between the
14//! channel layer and the cluster `sync` module.
15
16use crate::envelope::Envelope;
17
18/// Receives channel lifecycle events so an out-of-library clusterer can map them
19/// onto distributed process-group membership and cross-node fan-out.
20///
21/// Every method is called from the host thread that drove the originating
22/// [`ChannelHandle`](crate::channel::ChannelHandle) operation, AFTER the channel
23/// actor has applied it. Implementations must be cheap and non-blocking on the
24/// publish path; a slow observer would stall the publishing caller.
25pub trait ClusterObserver: Send + Sync + std::fmt::Debug {
26 /// A local subscriber (beamr pid `subscriber_pid`) joined `channel`.
27 fn on_subscribe(&self, channel: &str, subscriber_pid: u64);
28
29 /// A local subscriber (beamr pid `subscriber_pid`) left `channel`.
30 fn on_unsubscribe(&self, channel: &str, subscriber_pid: u64);
31
32 /// A message was published to `channel` on this node; the observer may fan it
33 /// out to remote subscribers.
34 fn on_publish(&self, channel: &str, envelope: &Envelope);
35}