RaftEx
⚠️ EXPERIMENTAL / UNDER ACTIVE DEVELOPMENT ⚠️
This library is an experimental Elixir port of the RabbitMQ RA Raft consensus algorithm. It is currently in early development and NOT READY FOR PRODUCTION USE.
- APIs are unstable and may change without notice
- Data formats are not finalized
- Critical edge cases may not yet be handled
- Distributed multi-node testing is incomplete
Use at your own risk for learning, testing, and evaluation purposes only.
Raft consensus algorithm implementation in Elixir, ported from the RabbitMQ RA library (original Erlang repo).
Overview
RaftEx implements the Raft consensus algorithm, providing:
- Leader Election: Pre-vote and candidate phases with quorum-based voting
- Log Replication: Write-ahead log (WAL) with batched writes and fsync support
- Snapshotting: Point-in-time state snapshots with chunked transfer
- Cluster Membership: Dynamic membership changes (join/leave)
- State Machine: Pluggable state machine interface for application logic
- Consistent Queries: Leader-verified reads with majority acknowledgement
Current Status
✅ Implemented
| Component | Status | Description |
|---|---|---|
| Types & RPCs | ✅ Complete | All Raft protocol message structs |
| WAL | ✅ Complete | Batched writes, fsync, recovery, truncation |
| Log | ✅ Complete | Append, read, fold, snapshot, config persistence |
| Metadata Store | ✅ Complete | DETS + ETS backed term/voted_for/last_applied |
| Election Logic | ✅ Complete | Pre-vote, candidate, vote handling, quorum eval |
| RPC Handlers | ✅ Complete | AppendEntries, InstallSnapshot, Heartbeat |
| Cluster Mgmt | ✅ Complete | Membership, voter status, match indexes |
| Effects System | ✅ Complete | Reply routing, notifications, machine effects |
| Tests | ✅ 85 passing | Unit tests for all major components |
🚧 In Progress
| Component | Status | Description |
|---|---|---|
| Server Process | 🚧 Partial | Leader/follower states work, candidate/pre_vote need completion |
| Segment Files | 🚧 Stub | Long-term log storage (WAL-only currently) |
| Snapshot Transfer | 🚧 Partial | Chunked sending logic needs full implementation |
| Network Layer | 🚧 Stub | Distributed RPC between nodes not yet wired |
| Integration Tests | 🚧 Partial | Single-node tests pass, multi-node tests needed |
❌ Not Started
| Component | Status | Description |
|---|---|---|
| Performance | ❌ TODO | gen_batch_server, optimizations |
| Metrics | ❌ TODO | Seshat integration incomplete |
| Documentation | ❌ TODO | API docs, guides, examples |
Installation
⚠️ This package is not yet published to Hex.pm due to its experimental status.
If available, add to your mix.exs:
def deps do
[
{:raft_ex, "~> 0.0.1"}
]
endOr use directly from Git:
def deps do
[
{:raft_ex, git: "https://github.com/your-org/raft_ex", branch: "main"}
]
endQuick Start
# Start the RaftEx system
RaftEx.start_in("/tmp/raft_data")
# Define a simple state machine
defmodule CounterMachine do
@behaviour RaftEx.Machine
@impl RaftEx.Machine
def init(_conf), do: 0
@impl RaftEx.Machine
def apply(_meta, {:increment}, state), do: {state + 1, state + 1}
def apply(_meta, {:get}, state), do: {state, state}
end
# Start a cluster (single node for testing)
server_id = {:server1, node()}
machine = {:machine, CounterMachine, %{}}
{:ok, _} = RaftEx.start_cluster(
:default,
:my_cluster,
machine,
[server_id]
)
# Send a command
{:ok, reply, _leader} = RaftEx.process_command(server_id, {:increment})
IO.inspect(reply) # => 1Architecture
┌─────────────────────────────────────────────────────────────┐
│ RaftEx.Server │
│ ┌─────────────┐ ┌──────────────┐ ┌────────────────────┐ │
│ │ Leader │ │ Follower │ │ Candidate │ │
│ │ State │ │ State │ │ State │ │
│ └──────┬──────┘ └──────┬───────┘ └────────┬───────────┘ │
│ │ │ │ │
│ ┌──────▼────────────────▼────────────────────▼───────────┐ │
│ │ Server Process (gen_statem) │ │
│ └──────────────────────┬───────────────────────────────┘ │
│ │ │
│ ┌──────────────────────▼───────────────────────────────┐ │
│ │ RaftEx.Log │ │
│ │ ┌────────────┐ ┌────────────┐ ┌───────────────┐ │ │
│ │ │ WAL │ │ Mem Table │ │ Snapshots │ │ │
│ │ │ (GenServer)│ │ (ETS) │ │ (Files) │ │ │
│ │ └────────────┘ └────────────┘ └───────────────┘ │ │
│ └──────────────────────────────────────────────────────┘ │
│ │ │
│ ┌──────────────────────▼───────────────────────────────┐ │
│ │ State Machine (Pluggable) │ │
│ └──────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘Key Components
RaftEx: Main public API for cluster and server managementRaftEx.ServerProc:gen_statemprocess managing server lifecycleRaftEx.Server: Core Raft protocol logic (state transitions, RPC handling)RaftEx.Log: Persistent replicated log with WAL + mem tableRaftEx.LogWal: Write-ahead log GenServer with batched fsyncRaftEx.LogMeta: DETS + ETS backed metadata storeRaftEx.Machine: Behaviour for pluggable state machinesRaftEx.Server.Election: Leader election with pre-vote supportRaftEx.Server.RpcHandler: AppendEntries, InstallSnapshot, Heartbeat handlers
Configuration
# System configuration
%{
name: :default,
data_dir: "/var/lib/raft_ex",
wal_max_size_bytes: 256_000_000,
wal_max_batch_size: 8192,
wal_sync_method: :datasync, # :fsync | :datasync | :none
wal_compute_checksums: true,
segment_max_entries: 4096,
segment_max_size_bytes: 64_000_000
}
# Server configuration
%{
id: {:server1, node()},
uid: "unique_id",
cluster_name: :my_cluster,
initial_members: [{:server1, node()}, {:server2, node()}],
machine: {:machine, MyMachine, %{}}
}Development
# Install dependencies
mix deps.get
# Run tests
mix test
# Run tests with coverage
mix test --cover
# Check formatting
mix format --check-formatted
# Generate documentation
mix docsContributing
This project is in early development. Contributions are welcome but please note:
- This is experimental software - expect breaking changes
- All contributions must include tests
-
Follow the existing code style (
mix format) -
Document public APIs with
@docand@moduledoc
License
Dual-licensed under:
- Mozilla Public License 2.0 (MPL-2.0)
- Apache License 2.0 (Apache-2.0)
See LICENSE, LICENSE-APACHE2, and LICENSE-MPL-RabbitMQ for details.
Acknowledgments
- Original implementation: RabbitMQ RA
- Raft paper: In Search of an Understandable Consensus Algorithm