RaftEx.Log
(raft_ex v0.1.0)
View Source
Persistent replicated Raft log.
This module manages the combination of WAL + segment files + mem tables. It provides the core log operations needed by the Raft consensus algorithm:
- Append: Add new entries to the log (via WAL)
- Read: Fetch entries by index range or specific indexes
- Snapshot: Install and recover snapshots
- Checkpoint: Create recovery checkpoints for crash recovery
- Configuration: Persist and read cluster configuration
Architecture
The log consists of several layers:
- WAL (Write-Ahead Log): Durable, sequential write buffer
- Mem Table: In-memory buffer for recent entries
- Segments: Immutable on-disk files for older entries
- Snapshot: Point-in-time state with metadata
Entry Format
Each log entry is a tuple: {index, term, command}
Where command can be:
{:"$usr", metadata, user_command, reply_mode}- User command{:"$ra_cluster_change", metadata, new_cluster, reply_mode}- Membership change{:noop, metadata, next_mac_ver}- No-op entry for leadership{:"$ra_leave", from, leaving, reply_mode}- Leave command{:"$ra_cluster", metadata, :delete, reply_type}- Delete cluster
Summary
Functions
Append a single entry to the log.
Check if the log can accept writes.
Create a checkpoint at the given index.
Close the log and clean up resources.
Delete all log data (used for cleanup).
Execute a read plan, fetching entries from disk as needed.
Check if an entry exists in the log.
Fetch the term for a specific index.
Fold over entries in the range [from, to].
Handle a log event (e.g., compaction notification).
Check if there are pending writes.
Initialize a new log instance.
Install a snapshot at the given index and term.
Get the last index and term in the log.
Get the last written index and term.
Get the next index that would be assigned.
Get an overview of the log state.
Build a read plan for partial reads.
Promote a checkpoint to be the new recovery point.
Read the cluster configuration from disk.
Get information about a read plan.
Recover snapshot state from disk.
Release resources (file handles, etc.).
Set the last index (used during recovery).
Set the snapshot state.
Get the current snapshot index and term.
Get the size of the current snapshot.
Get the current snapshot state.
Read specific entries by index.
Process a tick for log maintenance.
Truncate the log, removing all entries starting from (and including) from_index.
Used when a leader sends conflicting entries at a given index.
Update the release cursor for snapshot management.
Write multiple entries to the log.
Write the cluster configuration to disk.
Types
@type t() :: %RaftEx.Log{ cfg: map(), current_snapshot: {RaftEx.Types.index(), RaftEx.Types.term_num()} | nil, data_dir: Path.t(), entries: %{ required(RaftEx.Types.index()) => {RaftEx.Types.index(), RaftEx.Types.term_num(), term()} }, last_resend_time: non_neg_integer(), last_term: RaftEx.Types.term_num(), last_wal_write: {pid(), reference(), RaftEx.Types.index()}, last_written_index_term: {RaftEx.Types.index(), RaftEx.Types.term_num()}, live_indexes: [RaftEx.Types.index()], mem_table: :ets.table() | nil, meta_name: atom(), next_index: RaftEx.Types.index(), pending: [term()], range: {RaftEx.Types.index(), RaftEx.Types.index()} | nil, reader: term() | nil, snapshot_state: term() | nil, tx: boolean(), wal_name: atom() }
Functions
@spec append({RaftEx.Types.index(), RaftEx.Types.term_num(), term()}, t()) :: t()
Append a single entry to the log.
Returns the updated state. The entry is added to the mem table and scheduled for WAL write.
Check if the log can accept writes.
Create a checkpoint at the given index.
@spec close(t()) :: :ok
Close the log and clean up resources.
@spec delete_everything(t()) :: :ok
Delete all log data (used for cleanup).
Execute a read plan, fetching entries from disk as needed.
@spec exists( {RaftEx.Types.index(), RaftEx.Types.term_num()}, t() ) :: {boolean(), t()}
Check if an entry exists in the log.
@spec fetch_term(RaftEx.Types.index(), t()) :: {RaftEx.Types.term_num() | nil, t()}
Fetch the term for a specific index.
@spec fold( RaftEx.Types.index(), RaftEx.Types.index(), (term(), term() -> term()), term(), t(), atom() ) :: {term(), t()}
@spec fold( RaftEx.Types.index(), RaftEx.Types.index(), (term(), term() -> term()), term(), t(), atom() ) :: {term(), t()}
Fold over entries in the range [from, to].
Applies fun to each entry, accumulating acc. Returns {final_acc, state}.
Handle a log event (e.g., compaction notification).
Check if there are pending writes.
Initialize a new log instance.
Sets up the data directory, WAL, ETS table for mem table, and recovers from existing state if present.
@spec install_snapshot( {RaftEx.Types.index(), RaftEx.Types.term_num()}, module(), [RaftEx.Types.index()], t() ) :: {:ok, t(), [term()]}
Install a snapshot at the given index and term.
This clears all log entries up to and including the snapshot index.
@spec last_index_term(t()) :: {RaftEx.Types.index(), RaftEx.Types.term_num()}
Get the last index and term in the log.
@spec last_written(t()) :: {RaftEx.Types.index(), RaftEx.Types.term_num()}
Get the last written index and term.
@spec next_index(t()) :: RaftEx.Types.index()
Get the next index that would be assigned.
Get an overview of the log state.
@spec partial_read([RaftEx.Types.index()], t(), (RaftEx.Types.index(), RaftEx.Types.term_num(), term() -> term())) :: %{ dir: Path.t(), read: map(), plan: list() }
Build a read plan for partial reads.
Returns a map with :dir, :read, and :plan keys indicating
which entries are in memory vs need to be read from disk.
@spec promote_checkpoint(RaftEx.Types.index(), t()) :: {t(), [term()]}
Promote a checkpoint to be the new recovery point.
Read the cluster configuration from disk.
@spec read_plan_info(map()) :: %{ num_read: non_neg_integer(), num_in_segments: non_neg_integer(), num_segments: non_neg_integer() }
Get information about a read plan.
Recover snapshot state from disk.
@spec release_resources(non_neg_integer(), atom(), t()) :: t()
Release resources (file handles, etc.).
@spec set_last_index(RaftEx.Types.index(), t()) :: {:ok, t()}
Set the last index (used during recovery).
Set the snapshot state.
@spec snapshot_index_term(t()) :: {RaftEx.Types.index(), RaftEx.Types.term_num()} | nil
Get the current snapshot index and term.
@spec snapshot_size(t()) :: non_neg_integer() | nil
Get the size of the current snapshot.
Get the current snapshot state.
@spec sparse_read([RaftEx.Types.index()], t()) :: {[term()], t()}
Read specific entries by index.
Returns {entries, state} where entries is a list of {index, term, command} tuples.
@spec tick(non_neg_integer(), t()) :: t()
Process a tick for log maintenance.
@spec truncate(RaftEx.Types.index(), t()) :: t()
Truncate the log, removing all entries starting from (and including) from_index.
Used when a leader sends conflicting entries at a given index.
@spec update_release_cursor( RaftEx.Types.index(), atom(), {module(), term()}, term(), t() ) :: {t(), [term()]}
Update the release cursor for snapshot management.
@spec write([{RaftEx.Types.index(), RaftEx.Types.term_num(), term()}], t()) :: {:ok, t()} | {:error, term()}
Write multiple entries to the log.
Appends all entries and then flushes to WAL.
Returns {:ok, updated_state}.
Write the cluster configuration to disk.