package granary

  1. Overview
  2. Docs
Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source

Module Granary_recovery.RecoverySource

Offline, best-effort salvage of a damaged database file into a fresh, structurally-valid one — the corruption-recovery counterpart to the crash-recovery already performed on open.

Read-only on the source; emits a clean new file rather than repairing in place. Implementation phases:

1. scan_raw: Raw-page scan verifying CRC32 on every page and classifying by kind byte. Provides a trust map used later. 2. open_source: Attempt to open the damaged file via Store.open_block (handles crash recovery / WAL replay automatically). Load the catalog. 3. extract: Walk each table's tree via cursor, skip corrupt pages (flagged by the scan trust map), decode rows. 4. rebuild: Open a fresh Store and replay recovered rows through the normal put path, producing correct CRCs in a clean CoW B+-tree.

When the source headers are intact (the common case), phases 2–3 use the existing Store/Catalog infrastructure. When headers are corrupt, the tool falls back to raw-page catalog reconstruction from the redundant mirror (#174).

Sourcetype scanned_page = private {
  1. page_id : int64;
  2. kind : Granary_storage.Page.kind;
  3. crc_valid : bool;
  4. tag : int32;
}

A single page from the raw scan, annotated with CRC status and decoded page kind.

Sourceval pp_scanned_page : Format.formatter -> scanned_page -> unit

Pretty-print a one-line summary for a scanned page.

Sourcetype scan_result = {
  1. n_pages : int64;
  2. pages : scanned_page list;
  3. trusted_count : int;
  4. damaged_count : int;
}

Result of the raw-page scan phase.

Sourceval scan : read_page:(int64 -> Cstruct.t option Lwt.t) -> n_pages:int64 -> scan_result Lwt.t

scan ~read_page ~n_pages reads every page 0..n_pages-1 from the source, verifies its CRC32, decodes the kind byte, and returns the annotated list. Pages that fail to read are silently skipped.

Sourcetype recovered_row = {
  1. tree_id : int;
  2. key : bytes;
  3. value : bytes;
}

A row recovered and ready to write to the output store.

Sourcetype recover_result = {
  1. rows_recovered : int;
  2. pages_scanned : int64;
  3. pages_trusted : int;
  4. pages_damaged : int;
  5. pages_skipped : int;
  6. tables_recovered : int;
  7. tables_total : int;
}

Final result of a complete recovery run.

Sourceval pp_result : Format.formatter -> recover_result -> unit

Pretty-print the recovery summary.

Sourceval recover : read_page:(int64 -> Cstruct.t option Lwt.t) -> n_pages:int64 -> open_source: (unit -> (Granary_store.Store.t * Granary_catalog.Catalog.t, string) result Lwt.t) -> write_to:(recovered_row list -> (unit, string) result Lwt.t) -> (recover_result, string) result Lwt.t

recover ~read_page ~n_pages ~open_source ~write_to runs the full recovery pipeline.