docs: use the fine-tuned home-page hero screenshot.
Signed-off-by: 林晨 (Leo Cheng) chengkelfan@qq.com
Raft 共识算法的 MoonBit 实现,移植自 etcd-io/raft(Apache-2.0)。领导者选举、pre-vote、日志复制与冲突回退、快照压缩、joint consensus 成员变更、learner 节点、线性一致读、崩溃恢复;含确定性模拟测试框架(可注入分区/丢包/乱序/宕机)与内建安全性不变量校验。已发布至 mooncakes.io:moon add Lfan-ke/raft-moonbit
版权所有:中国计算机学会技术支持:开源发展技术委员会
京ICP备13000930号-9
京公网安备 11010802047560号
raft-moonbit
A production-grade Raft consensus library in MoonBit — a faithful, line-by-line port of
etcd-io/raft.▶ Live demo · Docs · API · Quickstart
The project home - a faithful MoonBit port of etcd’s raft. Click through to the live, in-browser demo.
Raft keeps a cluster of nodes agreeing on the order of a command log even when nodes crash and the network drops, delays and reorders messages — the foundation of the replicated state machines behind systems such as etcd, TiKV and Consul. This library ports the Go
etcd-io/raft(Apache-2.0) to MoonBit, carrying over its protocol core, storage model and test suite; see NOTICE for what is derived and what is new.It ships two ways to drive one consensus core:
run_election,replicate) that composes the RPC handlers into whole rounds — small and easy to read or embed; andRaftNode) that speaks only inMessages throughtickandstep, so a real transport — or the bundled deterministic simulator — can drive it exactly the way etcd separates protocol logic from I/O.Install
Quick example
Or run the bundled example — a five-node cluster elects, replicates, loses its leader and re-elects, printing the safety invariants at each step:
Deterministic output (same seed → same transcript)
Source:
cmd/example/main.mbt. The lower-levelNode/RaftNodeAPIs are used directly in the tests — seeraftnode_wbtest.mbt(message-driven) andcluster_wbtest.mbt(synchronous).New to the library?
examples/is a guided tour in five short, runnable programs - from a first election up to a replicated key-value store:Correctness
This is a line-by-line port, and it is verified as one. The porting census (
PORTING.md) tracks every upstreamTest*function and has noPARTIALorTODOrows left — every test that does not depend on Go’s runtime is ported assertion-for-assertion, with no simplified cases, no skipped table rows and no weakened assertions; each remainingN/A(a goroutine/channel shell, a benchmark, or a Go struct-memory-layout assert) states its MoonBit equivalent.Three independent methods cross-check behaviour against
etcd-io/raft@26647d5:moon check --deny-warn— CI fails the build if either coverage number regresses.difftest)RawNodeand this port, compared event-by-event with upstream pinned as a git submodule. Directory restructuring and idiomatic cleanup are held to zero trace drift.Together they surfaced 24 correctness defects in the consensus, log and storage layers — safety, liveness, behavioural and accounting — plus 2 default-configuration mismatches, each fixed under a red-then-green regression test that is still in the suite. Several defect classes were then made unrepresentable: narrowing a storage error to a single-variant type turned a whole class of mistaken
catchinto a compile error, and exhaustive matching flags any never-constructed variant at build time.Live demo — real consensus in your browser
▶ https://lfan-ke.github.io/raft-moonbit/demo.html
Five nodes, five Web Workers. Each worker instantiates its own copy of this consensus core compiled to WebAssembly, ticks on its own wall-clock timer, and talks to peers only by
postMessage. The main thread is the network — drop packets, add delay, split the cluster, isolate or crash the leader — and it holds no Raft state of its own. Elections race, messages reorder, nothing about the schedule is deterministic; a panel re-checks the safety invariants (one leader per term, committed prefixes agree) on every frame.Click Split 2 | 3 and you can watch two nodes lead different terms at once - and Election Safety still holds, because two leaders only contradict Raft if they share a term, and the stale one cannot reach a majority, so it cannot commit. Heal the partition and it steps down.
It is not a JavaScript re-implementation — messages cross the boundary as flat integers, node state is a JSON string read straight out of the wasm module’s linear memory, and every transition happens inside the same MoonBit code the tests exercise (
worker_driver.mbt). Honest scope: five workers on one machine model concurrency, not a distributed deployment, and a restarted node catches up from the leader since the workers have no persistent storage.Build and run the site locally
Workers
fetchthe wasm, so afile://URL will not work.Features
A complete Raft, not a sketch — click to collapse
conflict_indexhint for one-jump backoff and areject_indexthat keeps a reordered rejection from driving a spurious back-off; per-followerProgress(probe / replicate / snapshot) drives repair, including from heartbeat acks.compact, theInstallSnapshotRPC, and automatic snapshot fallback for a follower whose next entry was already compacted away.ConfChangeV2and auto-leave — C(old,new) needs a majority of both halves, and the leader appends the leave entry itself once it commits. A committed change reconfigures the running node: quorums resize, a leader that removed itself steps down, and an in-flight transfer to a removed target aborts.learners_next-staged demotion across a joint change.Inflightslimit, a byte cap per batch (MaxSizePerMsg), and a bound on the uncommitted tail (MaxUncommittedEntriesSize).raftLogsplit into stable storage and anunstabletail with in-progress bookkeeping and byte-level pagination, so a caller knows exactly what to persist and what to apply.RawNodewithReady/Advance: ask whether there is work, take a batch (entries to persist,HardState/SoftStateif changed, messages, committed entries, read states), do it, acknowledge — no threads, no async, exactly as etcd’s contract describes.HardState, an append-only write-ahead log (WalStore) with replay, and an etcd-styleMemoryStorageengine. Storage reads reportCompacted,UnavailableandSnapOutOfDateas distinct errors, so a caller can tell “send a snapshot” from “wait”.ReadOnlySafe(fresh quorum round-trip) and lease-based — plus check-quorum, which steps a leader down when it loses a majority and makes followers refuse disruptive votes.TimeoutNow, §3.10): the target is caught up first, proposals are blocked mid-transfer, and it aborts on timeout, step-down or removal.StateMachine,Transport,LogStoreandRaftStoragetraits, with a replicated key-value store as the worked example.Cluster): a single-seed discrete-time network that drops, delays, reorders, partitions and crashes/restarts nodes, with built-in safety-invariant checks and a suite of scenario and chaos tests.Architecture
The code follows the upstream
etcd-io/raftpackage layout so a reader can audit the port package-by-package. The rootraft.mbtis a pure facade that re-exports the public surface, so consumers write@raft.Xregardless of where a symbol lives.quorum/tracker/Progress(Probe / Replicate / Snapshot) and theInflightswindowraftpb/Entry,Message, RPCs,HardState,Snapshot,ConfState, entry sizingconfchange/ConfChangeand the configChangerstorage/MemoryStorage, the write-ahead log, and theLogStore/RaftStoragetraitslog/RaftLog, the unstable tail, term lookup and bounded slicescore/RaftNode/Nodestep & dispatch, election, replication, snapshots, ReadIndex, leader lease, check-quorum,RawNode/Ready,Config, and the simulatordemo/Cluster, one Web Worker per nodeLicense
Apache-2.0. See LICENSE and NOTICE. A MoonBit port of etcd-io/raft (Copyright 2015 The etcd Authors); the protocol core, storage model and test suite are derived from it. What this port adds is the MoonBit data model — algebraic data types and exhaustive matching in place of Go structs and switches — a deterministic simulation harness with built-in safety-invariant checks, and a WebAssembly browser demo that runs each node in its own Web Worker.