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.


Raft consensus algorithm implementation in Elixir, ported from the RabbitMQ RA library (original Erlang repo).

Overview

RaftEx implements the Raft consensus algorithm, providing:

Current Status

✅ Implemented

ComponentStatusDescription
Types & RPCs✅ CompleteAll Raft protocol message structs (AppendEntries, RequestVote, PreVote, InstallSnapshot, Heartbeat, Info)
WAL✅ CompleteBatched writes, fsync/datasync, recovery, truncation, checksums
Log✅ CompleteWrite-ahead ordering, append, read, fold, snapshot, config persistence, mem table
Metadata Store✅ CompleteDETS + ETS backed term/voted_for/last_applied persistence with proper cleanup
Election Logic✅ CompletePre-vote, candidate, vote handling, quorum evaluation, correct vote attribution
RPC Handlers✅ CompleteAppendEntries with term/log checks, InstallSnapshot, Heartbeat handlers
Cluster Mgmt✅ CompleteMembership, voter status, match indexes, peer state tracking
Effects System✅ CompleteReply routing, notifications, machine effects, consensus handling
Server Process✅ Completegen_statem with leader/follower/candidate/pre_vote/recover states, election tracking
Segment Files✅ CompleteImmutable segment files with fsync, write, read, seal, truncate, delete, recovery
State Machine✅ CompletePluggable machine behaviour with apply, tick, version, aux hooks
Network Layer✅ CompleteDistributed RPC routing, local/remote process communication, node monitoring
Development Tooling✅ Completeexcoveralls, dialyxir, credo, quality aliases, CI-ready config
Tests✅ 107 passingUnit + integration tests for all major components

🚧 In Progress

ComponentStatusDescription
Snapshot Transfer🚧 PartialChunked sending logic implemented, needs full distributed testing
Integration Tests🚧 PartialSingle-node + cluster membership tests pass, multi-node distributed tests needed
Log Segments🚧 PartialImplementation complete with fsync, needs index-based direct access optimization
Leadership Transfer🚧 StubGraceful leader handoff to target peer API exists, needs full implementation

❌ Not Started

ComponentStatusDescription
Performance❌ TODOgen_batch_server, pipelined RPCs, batch optimizations
Metrics❌ TODOSeshat metrics integration incomplete
Documentation❌ TODOAPI docs, guides, examples, architecture deep-dive
Consistent Queries❌ TODOLeader-verified reads with majority ack needs completion

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.2"}
]
end

Or use directly from Git:

def deps do
[
{:raft_ex, git: "https://github.com/your-org/raft_ex", branch: "main"}
]
end

Quick 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) # => 1

Architecture

┌─────────────────────────────────────────────────────────────┐
│ 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

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 docs

Contributing

This project is in early development. Contributions are welcome but please note:

  1. This is experimental software - expect breaking changes
  2. All contributions must include tests
  3. Follow the existing code style (mix format)
  4. Document public APIs with @doc and @moduledoc

License

Dual-licensed under:

See LICENSE, LICENSE-APACHE2, and LICENSE-MPL-RabbitMQ for details.

Changelog

See CHANGELOG.md for a full list of changes.

Acknowledgments