README

Lfan-ke/raft-moonbit/raftpb does not have a README file

#
AppendEntriesArgs

pub(all) struct AppendEntriesArgs {
term : UInt64
leader_id : String
prev_log_index : UInt64
prev_log_term : UInt64
entries : Array[Entry]
leader_commit : UInt64
} derive(Eq)

Arguments for the AppendEntries RPC (Raft §5.3), used by the leader both to replicate log entries and, with an empty entries, as a heartbeat.

#
AppendEntriesReply

pub(all) struct AppendEntriesReply {
term : UInt64
success : Bool
match_index : UInt64
conflict_index : UInt64
conflict_term : UInt64
reject_index : UInt64
} derive(Eq)

Reply to an AppendEntries RPC. success is true when the follower's log contained a matching entry at prev_log_index and the entries were stored. On success match_index is the highest index the follower now agrees on, so the leader can advance its progress without guessing. On rejection conflict_index is a hint at where the two logs diverge, letting the leader back off in one jump instead of decrementing nextIndex one entry at a time (the optimization sketched in Raft §5.3).

#
ConfState

pub(all) struct ConfState {
voters : Array[String]
learners : Array[String]
voters_outgoing : Array[String]
learners_next : Array[String]
auto_leave : Bool
} derive(Eq)

A membership snapshot as carried by a Raft snapshot (etcd's ConfState).

#
ConfState::empty

fn ConfState::empty() -> ConfState

The empty membership state, carried by a snapshot with no recorded configuration (a fresh node, or one written before ConfState existed).

#
ConfState::equivalent

fn ConfState::equivalent(self : ConfState, other : ConfState) -> Bool

Whether two ConfStates describe the same configuration (etcd's ConfState.Equivalent): the four id lists match as sets (order- and nil/empty-insensitive) and the auto-leave flags agree.

#
ConfState::is_empty

fn ConfState::is_empty(self : ConfState) -> Bool

Whether this conf state records no membership at all.

#
Entry

pub(all) struct Entry {
term : UInt64
index : UInt64
entry_type : EntryType
command : Bytes
} derive(Eq)

One command entry in the replicated log.

term is the leader's term — a monotonically increasing logical clock — at the moment the entry was created. index is the entry's 1-based position in the log; index 0 is the empty position before the first entry. entry_type tells the apply loop whether command is an application command or a membership change. command is the opaque payload handed to the state machine (or membership decoder) once the entry is committed.

#
Entry::conf

fn Entry::conf(term : UInt64, index : UInt64, command : Bytes) -> Entry

Build a configuration-change entry.

#
Entry::id

fn Entry::id(self : Entry) -> EntryId

The identity (term, index) of this entry.

#
Entry::is_conf_change

fn Entry::is_conf_change(self : Entry) -> Bool

Whether this entry carries a membership change.

#
Entry::normal

fn Entry::normal(term : UInt64, index : UInt64, command : Bytes) -> Entry

Build a normal application entry.

#
EntryId

pub(all) struct EntryId {
term : UInt64
index : UInt64
} derive(Eq)

Uniquely identifies a log entry by the term that first appended it and its index. There is only ever one leader per term and a leader never issues two entries at the same index, so (term, index) pins an entry down. Mirrors etcd's entryID.

#
EntryType

pub(all) enum EntryType {
Normal
ConfChange
} derive(Eq)

What a log entry carries. A Normal entry holds an opaque state-machine command; a ConfChange entry holds a serialized membership change that the consensus layer applies to the cluster configuration once the entry is committed (Raft §6). Keeping the kind on the entry lets the apply loop route each committed entry to the right place.

#
HardState

pub(all) struct HardState {
term : UInt64
vote : String?
commit : UInt64
} derive(Eq)

The small piece of durable state Raft must flush before answering any RPC: the current term, the vote cast in that term, and the commit index. etcd calls this the HardState; persisting it is what lets a node rejoin without violating the election-safety or state-machine-safety properties (§5.3).

#
HardState::initial

fn HardState::initial() -> HardState

The HardState a fresh node starts from: term 0, no vote, nothing committed.

#
HardState::is_empty

fn HardState::is_empty(self : HardState) -> Bool

Whether a HardState is the zero value (etcd's IsEmptyHardState): a fresh node that has neither voted nor committed anything has nothing to persist.

#
HeartbeatArgs

pub(all) struct HeartbeatArgs {
term : UInt64
leader_id : String
commit : UInt64
context : Bytes
} derive(Eq)

Arguments for the Heartbeat RPC (Raft §5.2): a leader's periodic liveness beat. It carries no entries — only the leader's commit index (capped at what the follower is known to hold) so a follower can advance its commit index between AppendEntries. Modelling it as a distinct message (rather than an empty AppendEntries) lets the leader treat a heartbeat acknowledgement as pure liveness, separate from log-matching.

#
HeartbeatReply

pub(all) struct HeartbeatReply {
term : UInt64
context : Bytes
} derive(Eq)

Reply to a Heartbeat RPC: the follower's term and the read-index context it is acknowledging (echoed verbatim).

#
InstallSnapshotArgs

pub(all) struct InstallSnapshotArgs {
term : UInt64
leader_id : String
last_index : UInt64
last_term : UInt64
offset : UInt64
data : Bytes
done : Bool
conf_state : ConfState
} derive(Eq)

Arguments for the InstallSnapshot RPC (Raft §7). A leader sends this when a follower has fallen so far behind that the entries it needs have already been compacted away. The whole state-machine image up to last_index is shipped in data; real deployments chunk it, which the offset/done pair leaves room for.

#
InstallSnapshotArgs::whole

fn InstallSnapshotArgs::whole(term : UInt64, leader_id : String, snapshot : Snapshot) -> InstallSnapshotArgs

Build InstallSnapshot arguments that carry a whole snapshot in one message (offset 0, done true), the common case for an in-memory transport.

#
InstallSnapshotReply

pub(all) struct InstallSnapshotReply {
term : UInt64
} derive(Eq)

Reply to an InstallSnapshot RPC. term lets a stale leader discover it has been superseded.

#
Message

pub(all) struct Message {
from : String
to : String
payload : Payload
force : Bool
}

A routed message: a payload with the ids of the sender and intended recipient. The step function consumes these and produces more of them; a transport (real or simulated) is only responsible for delivery.

#
Message::is_response

fn Message::is_response(self : Message) -> Bool

Whether this message is a response rather than a request. The simulator uses it only for readable traces.

#
Message::kind

fn Message::kind(self : Message) -> String

A short, stable tag for the payload kind, for logs and scenario traces.

#
Message::new

fn Message::new(from : String, to : String, payload : Payload, force? : Bool) -> Message

Build a routed message. force is set only for leadership-transfer votes.

#
Message::term

fn Message::term(self : Message) -> UInt64

The term the message was stamped with. Every payload carries the sender's term; a receiver uses it to decide whether to step down or reject.

#
Payload

pub(all) enum Payload {
PreVote(RequestVoteArgs)
PreVoteResp(RequestVoteReply)
Vote(RequestVoteArgs)
VoteResp(RequestVoteReply)
Append(AppendEntriesArgs)
AppendResp(AppendEntriesReply)
Heartbeat(HeartbeatArgs)
HeartbeatResp(HeartbeatReply)
Snapshot(InstallSnapshotArgs)
TimeoutNow(UInt64)
Propose(Array[Entry])
ReadIndex(Bytes)
ReadIndexResp(ReadIndexResp)
TransferLeader(String)
ForgetLeader
}

The body of a message exchanged between nodes. A heartbeat is modelled as an Append with no entries, so the same replication path carries both. Keeping every request paired with its response in one enum lets a transport move Raft traffic without knowing what any particular message means.

#
Payload::is_local

fn Payload::is_local(self : Payload) -> Bool

etcd classifies a handful of message types as node-local — MsgHup, MsgBeat, MsgSnapStatus, MsgCheckQuorum and the storage-thread messages — which never travel over the transport. This port has no such messages: those triggers are direct method calls (tick / campaign / step), not Payloads. Every transportable Payload is therefore, by construction, a network message.

#
ReadIndexResp

pub(all) struct ReadIndexResp {
term : UInt64
index : UInt64
context : Bytes
} derive(Eq)

The leader's answer to a forwarded ReadIndex (etcd's MsgReadIndexResp): the commit index the read is anchored at, echoed back with the caller's context to the server that originated the read. Unlike the request it carries the leader's term, so it is term-checked like any other reply.

#
RequestVoteArgs

pub(all) struct RequestVoteArgs {
term : UInt64
candidate_id : String
last_log_index : UInt64
last_log_term : UInt64
} derive(Eq)

Arguments for the RequestVote RPC (Raft §5.2), sent by a candidate to gather votes for the term it is standing in.

#
RequestVoteReply

pub(all) struct RequestVoteReply {
term : UInt64
vote_granted : Bool
} derive(Eq)

Reply to a RequestVote RPC. term lets a stale candidate discover it has been superseded; vote_granted is true when the vote was given.

#
Snapshot

pub(all) struct Snapshot {
last_index : UInt64
last_term : UInt64
data : Bytes
conf_state : ConfState
} derive(Eq)

A point-in-time snapshot of the state machine that stands in for the log prefix up to and including last_index. Raft uses snapshots to bound log growth and to bring a badly lagging follower up to date in one shot (§7).

#
Snapshot::empty

fn Snapshot::empty() -> Snapshot

The empty snapshot: no state captured, baseline at index 0.

#
Snapshot::is_empty

fn Snapshot::is_empty(self : Snapshot) -> Bool

Whether this snapshot actually captures any state. A baseline index of 0 is the empty snapshot a fresh node starts with.

#
entry_encoding_size

fn entry_encoding_size(e : Entry) -> UInt64

The protobuf wire size of one entry, following etcd's proto.Size: each non-zero field costs a tag byte plus its varint/length-delimited encoding. Only the byte total matters — it is what limit_size and Storage.Entries budget against.

#
ents_size

fn ents_size(entries : ArrayView[Entry]) -> UInt64

The total encoding size of a run of entries.

#
hard_state_equal

fn hard_state_equal(a : HardState, b : HardState) -> Bool

Whether two hard states are equal in all three durable fields — term, vote, and commit (etcd's isHardStateEqual). This is the test that keeps a Ready from carrying a redundant HardState when nothing durable has moved.

#
limit_size

fn limit_size(entries : ArrayView[Entry], max_size : UInt64) -> Array[Entry]

The longest prefix of entries whose total encoding size does not exceed max_size. Always returns at least one entry when the input is non-empty — so a single oversized entry is still returned — matching etcd's limitSize.

#
no_limit

let no_limit : UInt64

The sentinel used for an unbounded size cap: no entries/slice request is ever limited by size. Mirrors etcd's noLimit = math.MaxUint64.

#
payload_size

fn payload_size(e : Entry) -> UInt64

The size of an entry's payload: its command bytes only, independent of term and index. Empty-payload entries (like the no-op a new leader appends) are zero size, so they do not count against the uncommitted-log quota. Mirrors etcd's payloadSize.

#
payloads_size

fn payloads_size(entries : ArrayView[Entry]) -> UInt64

The total payload size of a run of entries (etcd's payloadsSize).