datum-cluster 0.10.3

Datum cluster membership over SWIM gossip
Documentation
//! Compile-checked snippet backing docs/guides/datum-cluster.md.
//! The region between the markers is imported verbatim via the VitePress `<<<` include.
//! Single loopback node only — no multi-node convergence is required in CI.

use std::time::Duration;

#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn cluster_quickstart_example() {
    // #region cluster-quickstart
    use datum_cluster::{ClusterConfig, ClusterNode, MemberState};

    // One member's configuration. Roles are advertised in gossip; seed nodes are
    // ADDRESSES only (identities are discovered through gossip). This example
    // starts a single loopback node with no seeds.
    let config = ClusterConfig::new("orders-1").with_roles(["orders", "eu-west"]);

    // Starting a node begins gossiping immediately and returns a live handle.
    let node = ClusterNode::start(config)
        .await
        .expect("cluster node starts");
    assert_eq!(node.node_id(), "orders-1");

    // Two membership feeds, both ordinary Datum concurrency primitives:
    //  - state():  a Signal<ClusterState>       — latest snapshot, no replay.
    //  - events(): a Subscription<MemberEvent>  — every transition, in order.
    let state = node.state();
    let _events = node.events();

    // The local member is in the snapshot immediately; peers converge async.
    let snapshot = node.current_state();
    assert_eq!(snapshot.self_node, "orders-1");
    assert!(snapshot.member("orders-1").is_some());

    // Wait for the local member to reach `Up`. With one member, this node is
    // then the deterministic placement coordinator (oldest Up member, by
    // incarnation then node id).
    loop {
        if state
            .get()
            .member("orders-1")
            .is_some_and(|member| member.state == MemberState::Up)
        {
            break;
        }
        tokio::time::sleep(Duration::from_millis(10)).await;
    }
    assert!(node.current_state().is_placement_coordinator("orders-1"));

    // Graceful leave: Leaving -> Exiting -> Removed, gossiped to peers.
    node.leave().await.expect("graceful leave");
    // #endregion cluster-quickstart
}