d_engine_server/membership/membership_snapshot.rs
1use std::collections::BTreeSet;
2
3/// A point-in-time snapshot of committed cluster membership.
4///
5/// Delivered via [`crate::EmbeddedEngine::watch_membership`] whenever a `ConfChange`
6/// entry commits. The snapshot reflects the membership state **after** the
7/// change has been applied, so `borrow()` always returns a consistent view.
8///
9/// ## Idempotency
10///
11/// `committed_index` is the Raft log index of the `ConfChange` entry that
12/// triggered this snapshot. It is strictly monotonically increasing across
13/// snapshots and can be used as an idempotency key:
14///
15/// ```ignore
16/// if snapshot.committed_index <= self.last_applied {
17/// return; // already handled
18/// }
19/// self.last_applied = snapshot.committed_index;
20/// scheduler.rebalance(&snapshot);
21/// ```
22///
23/// ## Diff computation
24///
25/// No diff fields are included. Because `watch::channel` is lossy (only the
26/// latest value is retained), a diff embedded in the snapshot could be stale
27/// if the receiver is slow and skips an intermediate change. Callers that
28/// need a diff should compute it against their own previous snapshot:
29///
30/// ```ignore
31/// let prev = std::mem::replace(&mut self.prev_snapshot, snapshot.clone());
32/// let joined: BTreeSet<_> = snapshot.members.difference(&prev.members).copied().collect();
33/// let left: BTreeSet<_> = prev.members.difference(&snapshot.members).copied().collect();
34/// ```
35#[derive(Debug, Clone, PartialEq, Eq, Default)]
36pub struct MembershipSnapshot {
37 /// Current voting members (Follower / Leader role, Active status).
38 pub members: BTreeSet<u32>,
39
40 /// Current non-voting learners (Learner role, Promotable or ReadOnly status).
41 pub learners: BTreeSet<u32>,
42
43 /// Raft log index of the ConfChange entry that produced this snapshot.
44 /// Monotonically increasing; use as an idempotency key.
45 pub committed_index: u64,
46}