Fact.WriteAheadLog (Fact v0.3.1)
View SourceA segmented, append-only write-ahead log for durability and crash recovery.
Fact.WriteAheadLog records every event write as a binary entry before it is committed to the
ledger and indexes. On crash, the Fact.EventLedger replays uncommitted WAL entries to restore
the database to a consistent state.
The WAL is organized as a series of numbered segment files. When the active segment exceeds
:max_file_size, it is rotated and a new segment is opened. Old segments beyond :max_segments
are deleted automatically.
Checkpoint entries can be written to mark a known-good recovery point. During recovery, replay begins from the most recent checkpoint rather than scanning the entire log.
A periodic sync timer flushes the write buffer to disk at the configured :sync_interval.
When :enable_fsync is true, each sync calls fsync to guarantee durability.
This process is started and supervised by Fact.DatabaseSupervisor and is not intended to be
started directly. Configuration options can be passed through Fact.open/2 via the :wal key.
Configuration
See wal_option/0 for available options and their defaults.
Summary
Functions
Returns a specification to start this module under a supervisor.
Flushes the write buffer and closes the active segment file.
Writes a checkpoint entry to the write-ahead log.
Reads all entries from every segment in the write-ahead log.
Reads entries from segments at or after the given segment offset.
Repairs the most recent WAL segment by discarding corrupt trailing entries.
Starts a Fact.WriteAheadLog process linked to the calling process.
Flushes the write buffer to disk.
Appends a binary entry to the write-ahead log.
Types
@type option() :: {:database_id, Fact.database_id()} | {:name, GenServer.name()} | wal_option()
Options accepted by start_link/1.
:database_id- (required) The database identifier used to scope the WAL directory and process registration.:name- (required) The registered process name, typically constructed viaFact.Registry.via/2.:enable_fsync- Seewal_option/0.:max_file_size- Seewal_option/0.:max_segments- Seewal_option/0.:sync_interval- Seewal_option/0.
@type wal_option() :: {:enable_fsync, boolean()} | {:max_file_size, pos_integer()} | {:max_segments, pos_integer()} | {:sync_interval, pos_integer()}
Write-ahead log configuration options.
:enable_fsync- Whether to call fsync after writes. Defaults totrue.:max_file_size- Maximum size in bytes of a WAL segment file before rotation. Defaults to16_777_216(16 MB).:max_segments- Maximum number of segment files to retain. Defaults to4.:sync_interval- Time in milliseconds between periodic sync operations. Defaults to200. Values below10are clamped to10to prevent mailbox flooding.
Functions
Returns a specification to start this module under a supervisor.
See Supervisor.
@spec close(Fact.database_id()) :: :ok
Flushes the write buffer and closes the active segment file.
@spec create_checkpoint(Fact.database_id(), binary()) :: :ok | {:error, term()}
Writes a checkpoint entry to the write-ahead log.
A checkpoint marks a known-good recovery point. During crash recovery, replay begins from the most recent checkpoint rather than the beginning of the log. The buffer is flushed to disk immediately after a checkpoint is written.
@spec read_all(Fact.database_id(), boolean()) :: [Fact.WriteAheadLog.Entry.t()]
Reads all entries from every segment in the write-ahead log.
When from_checkpoint? is true, only entries written after the most recent checkpoint
are returned. When false, all entries across all segments are returned in order.
@spec read_all_from_offset(Fact.database_id(), non_neg_integer(), boolean()) :: [ Fact.WriteAheadLog.Entry.t() ]
Reads entries from segments at or after the given segment offset.
Behaves like read_all/2 but skips segments with an index lower than offset.
@spec repair(Fact.database_id()) :: {:ok, list()} | {:error, :no_segments}
Repairs the most recent WAL segment by discarding corrupt trailing entries.
Reads the newest segment file entry-by-entry. Valid entries (those that pass CRC verification) are kept; the first corrupt entry and everything after it are discarded. The repaired segment is written back to disk in place.
@spec start_link([option()]) :: GenServer.on_start()
Starts a Fact.WriteAheadLog process linked to the calling process.
Requires :database_id and :name in opts. Additional wal_option/0 keys are optional.
@spec sync(Fact.database_id()) :: :ok
Flushes the write buffer to disk.
@spec write_entry(Fact.database_id(), binary()) :: :ok | {:error, term()}
Appends a binary entry to the write-ahead log.
The entry is written to the active segment file. If the segment exceeds :max_file_size,
a rotation occurs before the write.