Write-Ahead Log (WAL)
This page documents the WAL (Write-Ahead Log) layer in the sync module.
The WAL is a foundational durability primitive used to persist state transitions before they are applied or synchronized.
It guarantees:
- Append-only writes
- Monotonic offsets
- Deterministic replay
- Crash recovery safety
Headers
#include <vix/sync/wal/Wal.hpp>
#include <vix/sync/wal/WalWriter.hpp>
#include <vix/sync/wal/WalReader.hpp>
#include <vix/sync/wal/WalRecord.hpp>Namespace:
vix::sync::walArchitecture Overview
The WAL layer is composed of:
- Wal: high-level append + replay interface
- WalWriter: append-only record writer
- WalReader: sequential forward reader
- WalRecord: immutable record structure
- RecordType: durable state transition classification
The WAL is append-only and does not modify past entries.
Wal
Wal provides a minimal high-level interface.
class Wal
{
public:
struct Config
{
std::filesystem::path file_path{"./.vix/wal.log"};
bool fsync_on_write{false};
};
explicit Wal(Config cfg);
std::int64_t append(const WalRecord& rec);
std::int64_t replay(
std::int64_t from_offset,
const std::function<void(const WalRecord&)>& on_record);
};Guarantees
- Each
append()returns a monotonically increasing offset replay()processes records strictly in append order- Replay is deterministic
- Durability depends on
fsync_on_write
WalRecord
WalRecord describes a durable state transition.
enum class RecordType : std::uint8_t
{
PutOperation = 1,
MarkDone = 2,
MarkFailed = 3
};struct WalRecord
{
std::string id;
RecordType type{RecordType::PutOperation};
std::int64_t ts_ms{0};
std::vector<std::uint8_t> payload;
std::string error;
std::int64_t next_retry_at_ms{0};
};Meaning
- id: operation identifier
- type: transition category
- ts_ms: creation timestamp
- payload: serialized Operation (for PutOperation)
- error: failure diagnostics (for MarkFailed)
- next_retry_at_ms: retry scheduling timestamp
WalRecord is immutable after append.
WalWriter
Append-only writer.
class WalWriter
{
public:
struct Config
{
std::filesystem::path file_path;
bool fsync_on_write{false};
};
explicit WalWriter(Config cfg);
~WalWriter();
std::int64_t append(const WalRecord& rec);
void flush();
};Behavior:
- Opens file lazily
- Appends serialized records sequentially
- Returns byte offset of appended record
- Optional fsync for stronger crash safety
WalReader
Sequential forward reader.
class WalReader
{
public:
explicit WalReader(std::filesystem::path file_path);
void seek(std::int64_t offset);
std::optional<WalRecord> next();
std::int64_t current_offset() const noexcept;
};Behavior:
- Forward-only iteration
- Lazy file open
- Offset-based replay
- Used during recovery
Typical Usage
Append before side effects
Wal wal({ "./.vix/wal.log", true });
WalRecord rec;
rec.id = "op-123";
rec.type = RecordType::PutOperation;
rec.ts_ms = now_ms;
auto offset = wal.append(rec);After append succeeds, external effects may proceed.
Replay during recovery
wal.replay(0, [](const WalRecord& rec)
{
// Reconstruct Outbox state
});Replay restores state in exact original order.
Crash Recovery Model
WAL ensures:
- No committed state transition is lost
- On restart, replay rebuilds consistent state
- In-flight operations can be reconstructed
- Ordering is preserved
WAL is foundational for:
- Outbox durability
- Sync engine convergence
- Deterministic recovery
Durability Model
Durability depends on configuration:
fsync_on_write = false- Fast writes
- OS-level buffering
- Risk window on sudden power loss
fsync_on_write = true- Stronger crash guarantees
- Slower writes
Applications choose tradeoff.
Offline-First Guarantees
WAL enforces one critical invariant:
Every state transition is persisted before it can affect the outside world.
This ensures:
- Local writes are durable
- Replay is deterministic
- Network failures never corrupt state
- Crash recovery is correct
Design Philosophy
The WAL layer is:
- Append-only
- Minimal
- Deterministic
- Explicit
- Transport-independent
It is the lowest-level durability primitive in the sync stack.