Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Group Status

The Group<M> handle exposes a rich reactive API for monitoring group state: leadership, log progress, and online status.

Inspecting Current State

// Current leader (if any)
let leader: Option<PeerId> = group.leader();

// Am I the leader?
let is_leader: bool = group.is_leader();

// Group identity
let group_id: &GroupId = group.id();

// Current committed index
let committed: Index = group.committed();

// Current log position (may include uncommitted entries)
let cursor: Cursor = group.log_position();

// Active bonds (peer connections)
let bonds: Bonds = group.bonds();

// Group configuration
let config: &GroupConfig = group.config();

The When API

group.when() returns a &When handle for awaiting state transitions.

Leadership Events

// Wait for any leader to be elected
let leader: PeerId = group.when().leader_elected().await;

// Wait for a different leader (leader change)
let new_leader: PeerId = group.when().leader_changed().await;

// Wait until a specific peer becomes leader
group.when().leader_is(expected_peer_id).await;

// Wait until the local node becomes leader
group.when().is_leader().await;

// Wait until the local node becomes a follower
group.when().is_follower().await;

Online / Offline

A node is online when:

  • It is the leader, or
  • It is a fully synced follower (not catching up, not in an election)
// Wait until ready to process commands
group.when().online().await;

// Wait until the node goes offline
group.when().offline().await;

Log Position Watchers

The CursorWatcher type provides fine-grained observation of log progress:

Committed Index

let committed = group.when().committed();

// Wait until a specific index is committed
committed.reaches(42).await;

// Wait for any forward progress
let new_pos = committed.advanced().await;

// Wait for any change (including backward, e.g., log truncation)
let new_pos = committed.changed().await;

// Wait for regression (rare — happens during partition healing)
let new_pos = committed.reverted().await;

You can also pass an IndexRange from execute_many / feed_many:

let range = group.feed_many(commands).await?;
group.when().committed().reaches(range).await;

Log Position

The same API works for the full log position (including uncommitted entries):

let log = group.when().log();

let new_cursor = log.advanced().await;
let new_cursor = log.changed().await;

Index and Cursor Types

/// A log index (0-based, where 0 is the sentinel "no entry")
pub struct Index(pub u64);

/// A Raft term number
pub struct Term(pub u64);

/// A (term, index) pair identifying a specific log position
pub struct Cursor { pub term: Term, pub index: Index }

Both Index and Term provide:

  • zero(), one() — constants
  • is_zero() — check for sentinel
  • prev(), next() — saturating arithmetic
  • distance(other) — absolute difference

Bonds

group.bonds() returns a Bonds handle — a watchable, ordered collection of all active bonds:

let bonds = group.bonds();

// Iterate current bonds
for bond in bonds.iter() {
    println!("Bonded to: {}", bond.peer_id());
}

Bonds carry Raft messages, heartbeats, and sync traffic between group members. See the Bonds deep dive for internals.

Putting It Together

A common pattern for group-aware applications:

let group = network.groups()
    .with_key(key)
    .with_state_machine(MyMachine::new())
    .join();

// Wait until we can serve traffic
group.when().online().await;

if group.is_leader() {
    // Run leader-specific tasks
    run_leader_loop(&group).await;
} else {
    // Run follower-specific tasks
    run_follower_loop(&group).await;
}

// React to leadership changes
loop {
    let new_leader = group.when().leader_changed().await;
    if group.is_leader() {
        // Transition to leader role
    } else {
        // Transition to follower role
    }
}