Write-Ahead Log for fast strong consistency.
Replaces the heavy Three-Phase Commit (3PC) protocol with a lighter-weight WAL-based approach that:
- Writes to local ETS immediately
- Appends operation to WAL (in-memory ETS for speed)
- Async replicates WAL entries to replicas
- Tracks acknowledgments from replicas
- Returns success once majority has acked
This reduces strong-mode latency from ~1500µs (3PC) to ~200µs (WAL).
Design
- WAL entries are stored in an ETS table for fast access
- Each entry has a monotonically increasing sequence number
- Replicas ack entries asynchronously via
:erpc.cast - Majority acknowledgment determines commit success
- Periodic cleanup of committed entries
- Recovery on node restart replays uncommitted entries
Usage
This module is called internally by the Replicator when
replication_mode is set to :strong. You should not call it directly.
Configuration
The WAL uses sensible defaults but can be tuned via application config:
config :super_cache, :wal,
majority_timeout: 2_000, # ms to wait for majority ack
cleanup_interval: 5_000, # ms between cleanup cycles
max_pending: 10_000 # max uncommitted entries before backpressureExample
# Called internally by Replicator.replicate/3
SuperCache.Cluster.WAL.commit(2, [{:put, {:user, 1, "Alice"}}])
# => :ok
Summary
Functions
Handle replication acknowledgment from a replica.
Returns a specification to start this module under a supervisor.
Commit operations via WAL.
Recover uncommitted WAL entries after restart.
Apply WAL operations on a replica and acknowledge.
Starts the WAL GenServer.
Return WAL statistics.
Functions
@spec ack(non_neg_integer(), node()) :: :ok
Handle replication acknowledgment from a replica.
Called via :erpc.cast on the primary node when a replica has applied the WAL entry.
Returns a specification to start this module under a supervisor.
See Supervisor.
@spec commit(non_neg_integer(), [{atom(), any()}]) :: :ok | {:error, term()}
Commit operations via WAL.
- Writes to local ETS immediately
- Appends to WAL
- Async replicates to replicas
- Waits for majority acknowledgment
- Returns
:okon success,{:error, reason}on failure
This is the fast path for strong consistency — typically ~200µs vs ~1500µs for 3PC.
Example
WAL.commit(2, [{:put, {:user, 1, "Alice"}}])
# => :ok
@spec recover() :: :ok
Recover uncommitted WAL entries after restart.
Replays any entries that haven't been fully committed to ensure consistency.
@spec replicate_and_ack(non_neg_integer(), non_neg_integer(), [{atom(), any()}]) :: :ok
Apply WAL operations on a replica and acknowledge.
Called via :erpc.cast from the primary. Applies the operations locally
then sends an ack back to the primary.
Starts the WAL GenServer.
@spec stats() :: %{pending: non_neg_integer(), acks_pending: non_neg_integer()}
Return WAL statistics.
Example
WAL.stats()
# => %{pending: 42, committed: 1000}