package granary
Install
dune-project
Dependency
Authors
Maintainers
Sources
sha256=8b18780ea373be48301d9f333925860a2f9110fc0ac28684295118d72b65a67e
sha512=25ca3c9c5e2b528704a542502e0f37dc33ba003f65622d969b8c2b800778585f8ef0cf89b36e6679832e3993e8303aecddfc662742baf7044d6afe4a796b8f11
doc/granary.storage/Granary_storage/Wal/index.html
Module Granary_storage.WalSource
Write-Ahead Log over byte-addressable storage.
Frames are 4096-byte pages tagged with metadata; the WAL is a sequence of frames followed by an optional uncommitted tail. Each batch ends with a frame that has the commit bit set; recovery on open scans forward and accepts only frames whose commit batch is complete.
Layout ------ The WAL begins with a 24-byte header containing a magic identifier, a 64-bit salt assigned at open time when the WAL is empty, and a 64-bit seed used by the per-frame checksum. Frame i occupies header_size + i * frame_size_bytes bytes.
Per-frame layout: offset 0 page_id : uint64 big-endian offset 8 flags : uint64 big-endian (bit 0 = commit marker) offset 16 checksum: uint64 big-endian (FNV-1a-64 of preceding fields || salt || seed || page bytes) offset 24 page : 4096 bytes
Crash-safety guarantee ---------------------- A frame is considered durable only if (a) its checksum verifies and (b) some later frame in the same forward scan has the commit bit set without an intervening checksum failure. Partial trailing batches are silently discarded on recovery and overwritten by the next append.
This module is purely an append-only frame store; integration with the pager (read-from-WAL routing, checkpointing back to the main DB) lives in the Granary_store.Store module.
Pretty-print committed-frame count and current WAL size in bytes.
Size of one WAL frame for the DEFAULT 4096-byte geometry (24-byte frame header + 4096-byte page = 4120). A WAL opened with a non-default page_size uses a proportionally larger frame internally (#95).
Size of a WAL frame header in bytes (24).
val open_ :
?cipher:Crypto.t option ->
?page_size:int ->
?frame_cache_capacity:int ->
read_at:(offset:int64 -> Cstruct.t -> (unit, string) result Lwt.t) ->
write_at:(offset:int64 -> Cstruct.t -> (unit, string) result Lwt.t) ->
sync:(unit -> (unit, string) result Lwt.t) ->
size_bytes:int64 ->
unit ->
(t, error) result Lwt.tOpen a WAL over the given byte-addressable callbacks. If the device is empty (or smaller than header_size_bytes) the WAL is initialised with a fresh salt and seed. Otherwise the header is read, and a forward scan recovers the index of every page in the last contiguous committed batch. A trailing partial batch is silently discarded.
page_size (default Geometry.default's 4096) sets the frame's page payload size; it must match the main DB geometry (#95).
cipher (default None): when supplied, the page payload of each frame is AES-256-GCM encrypted; the 24-byte meta header stays plaintext so keyless recovery forward-scans still work. The FNV checksum covers the whole payload region (ciphertext + nonce + tag). Encrypted frames are Crypto.overhead (32) bytes larger than plaintext frames.
Number of successful device syncs since this WAL was opened. Exposed for #77 group-commit testing: tracks how many fsyncs the coordinator has issued.
Most recent committed frame index for page_id, or None if absent.
Snapshot-aware lookup: most recent frame index for page_id strictly less than max_frame. Used by RO snapshots so a reader captured at committed_frames = max_frame does not see frames written after. Returns None if no frame for page_id satisfies the bound.
Read the decrypted page bytes at a given frame index.
Ownership (#246): the returned Cstruct may be a cache-shared buffer that the WAL retains in its decrypted-frame cache and hands to other readers. The caller MUST treat it as read-only — cstruct_dup it before any in-place mutation, or only borrow it read-only and drop it before the next reset/checkpoint. Mutating it in place would corrupt the cache and every concurrent borrower. (The buffer is immutable for the life of the WAL generation; the cache is dropped wholesale on reset.)
Read the full frame metadata AND decrypted page bytes at a given committed frame index. Like read_frame but also returns the page_id and is_commit flag so the caller can reconstruct the full frame record (e.g. for incremental backup / frame capture).
Raises Corrupt_frame for indices outside 0, committed_frames).
Append a batch of pages; the LAST entry in the list is automatically marked as the commit frame. Performs a single sync at the end and only then updates the in-memory index. If sync fails the index is left unchanged so the partial batch is invisible to readers.
Append a batch of pages but skip the trailing sync. The in-memory index and committed_frames are bumped immediately so concurrent writers (still under the store's rw_mutex) can locate the newly written frames and subsequent appends place their frames at the correct base. Durability is deferred to a separate flush_sync call by the group-commit coordinator. Sync failure is treated as fatal by upstream callers — see Granary_store.Store.commit.
Invoke the underlying device sync. Used by the group-commit coordinator after one or more append_commit_no_sync calls.
Reset the WAL: discards all committed frames and the in-memory index. Used by checkpointing to truncate the log after migrating its contents to the main DB. The on-disk WAL is not physically truncated; later appends overwrite from the beginning. Bumps epoch so that replication consumers can detect that their snapshot has been invalidated.
WAL generation counter — bumped every time reset is called (i.e. after each checkpoint). Starts at 0 on open. Replication can use this to detect whether a checkpoint expired its snapshot.
Iterate over every (page_id, frame_idx) currently in the WAL index. Order is unspecified.