Skip to main content

load_snapshot

Function load_snapshot 

Source
pub fn load_snapshot(path: &Path) -> Result<MaterializedState>
Expand description

Load a snapshot, refusing anything this build cannot read (§5.5, D-043).

The header is checked before the payload is decompressed, and a mismatch is DbError::SnapshotIncompatible rather than a corruption error, because the two want opposite responses: corruption is a fault to report, an incompatible snapshot is an ordinary consequence of upgrading and the right answer is to discard it and cold-fold. Distinguishing them is the whole point of the header — bincode is not self-describing, so without one an old file does not reliably fail to parse, it parses into wrong values.

Headerless files written by 0.5.4 and earlier are rejected by the same path: their first four bytes are zstd’s magic, which is not MACR.

§Damage is a third answer, and it is bounded (0.13.12, W8.2, D-185)

DbError::SnapshotCorrupt is not DbError::SnapshotIncompatible and not DbError::ReplayCorrupt: the file is damaged, the ledger is not, and the repair is to delete the file. Every failure below used to be ReplayCorrupt { seq: 0 }, which said the log was damaged and carried a sequence number that cannot exist.

The checks run in the order that lets the cheapest one fire first, and each is a named error rather than a symptom further down:

  1. Declared payload length against the bytes actually present. Catches truncation and trailing junk without hashing anything.
  2. Checksum over the header and the payload, before zstd is handed a single byte. This is the check that closes §3.3: a corrupt stream is refused as a corrupt stream, rather than being walked to exhaustion by a deserializer trying to make sense of it.
  3. Declared plaintext length, enforced during decompression rather than checked after it — the reader is bounded to plain_len + 1 bytes, so a frame that expands further stops at the bound instead of allocating.
  4. A bincode limit equal to the buffer’s own size, replacing the Infinite limit bincode::deserialize carries.

Steps 3 and 4 are redundant with step 2 for every file this crate wrote, and that is the point of having them: they hold when the checksum has already been satisfied by something that computed it deliberately.

§This blocks (0.13.11, W8.1)

Read, decompress, deserialize, all synchronous — see save_snapshot for the argument. The crate’s one async reader is snapshot_anchor, which offloads the whole scan rather than each file.