datum_cluster/lib.rs
1#![forbid(unsafe_code)]
2//! Cluster membership for Datum.
3//!
4//! This crate is intentionally a membership layer only. It wraps `foca` 1.x as
5//! the private SWIM engine and keeps all foca identities, configuration, timers,
6//! and notifications out of the public API. The public model mirrors the useful
7//! Akka Cluster member states Datum needs before placement arrives:
8//! [`MemberState::Joining`], [`MemberState::Up`], [`MemberState::Leaving`],
9//! [`MemberState::Exiting`], [`MemberState::Down`], and
10//! [`MemberState::Removed`].
11//!
12//! The implementation is split into two planes. The membership actor owns the
13//! registry, state transitions, [`Signal`] current-state publication, lossless
14//! [`Subscription`] event publication, and downing decisions. The gossip plane is
15//! one Tokio task that owns the UDP socket and foca instance; it receives
16//! datagrams, drains foca runtime sends/timers/notifications, and never spawns an
17//! actor per packet.
18//!
19//! Current-state reads use `Signal<ClusterState>` because operators usually want
20//! the latest immutable view without replay. Membership changes use
21//! `Subscription<MemberEvent>` because active subscribers must observe every
22//! accepted state transition in sequence. `Subscription` is bounded and
23//! backpressures the publisher instead of dropping membership events.
24//!
25//! Failure detection and downing are deliberately separate. Foca suspicion marks
26//! a member unreachable; a [`DowningProvider`] decides when that unreachable
27//! member becomes [`MemberState::Down`]. The default [`TimeoutDowning`] is simple
28//! and useful for tests and small deployments, but it is not a split-brain
29//! resolver: v0.10 has no quorum, lease, or majority strategy. A split-brain
30//! resolution provider is a named follow-up before placement can make strong
31//! singleton or sharding claims.
32//!
33//! C3 placement uses a deterministic coordinator convention rather than an
34//! election protocol: every node picks the oldest reachable `Up` member from
35//! its local C1 view, tie-broken by node id. C1 does not assign Akka up numbers,
36//! so v0.10 uses the propagated member incarnation as the age key. This is
37//! deterministic for a shared view, but it is not fencing: under a partition
38//! plus timeout downing, both sides can transiently have a coordinator. Quorum
39//! or lease fencing is a v0.11+ follow-up.
40//!
41//! UDP is the v0.10 gossip transport. QUIC plus mTLS is deferred for the control
42//! plane and future secure gossip transport; the public API is transport-neutral
43//! so that change does not expose foca internals.
44
45mod config;
46mod downing;
47mod error;
48mod foca_driver;
49mod model;
50mod node;
51
52pub use config::ClusterConfig;
53pub use downing::{DowningProvider, TimeoutDowning};
54pub use error::{ClusterError, ClusterResult};
55pub use model::{ClusterState, Member, MemberEvent, MemberEventKind, MemberState};
56pub use node::ClusterNode;
57
58pub use datum::{Signal, Subscription};
59
60/// The `datum-cluster` crate version.
61pub const VERSION: &str = env!("CARGO_PKG_VERSION");