Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
bitcoinleveldb-snapshot
Low-level snapshot list implementation for the LevelDB-inspired storage engine used in bitcoin-rs.
This crate provides the internal snapshot representation for a Bitcoin-oriented fork/port of LevelDB. It exposes:
- An abstract
Snapshottrait (immutable database view) - A
SnapshotImpldoubly-linked node storing aSequenceNumber - A
SnapshotListthat maintains a heap-pinned sentinel and a monotonically ordered list of snapshots
It is primarily intended for internal consumption by the broader bitcoinleveldb-* ecosystem rather than for direct end-user use. Nevertheless, the code is fully documented and usable as a general-purpose, pointer-based snapshot list.
Conceptual model
In an LSM-based key–value store (such as LevelDB and its descendants), each mutation is assigned a monotonically increasing SequenceNumber. A snapshot is a logical view of the database at a specific sequence number. Reads performed under a snapshot must see all writes with sequence_number <= snapshot_sequence, and none of the later writes.
This crate implements the bookkeeping for those snapshots:
- Snapshots are represented as nodes in a circular doubly-linked list.
- The list has a heap-allocated sentinel head whose address is stable, even if the
SnapshotListitself moves. - Snapshots are ordered by their
SequenceNumberand appended in monotonically non-decreasing order. - Raw pointers (
*mut SnapshotImpl) are used instead ofRc/Arcto closely match LevelDB's original design and to minimize overhead.
Safety and invariants
The design relies on several invariants, enforced by debug assertions where possible:
- The sentinel head is always self-linked:
head.prev == headandhead.next == headfor an empty list. - For a non-empty list,
head.nextpoints to the oldest snapshot andhead.prevto the newest snapshot. SnapshotList::newmust be called with monotonically increasingSequenceNumbers. A debug assertion checks this when the list is non-empty.SnapshotList::deletemust never be called with a pointer to the sentinel head.- In non-
NDEBUGbuilds, eachSnapshotImplcontains a back-pointer to its owningSnapshotList, anddeletevalidates that pointer.
All list links are manipulated via raw pointers under unsafe blocks. From the perspective of the public API, the operations are logically safe provided that:
- Callers do not hold onto snapshot pointers after they have been passed to
delete. - Snapshots are not concurrently modified (they represent an immutable DB view, but their list links are mutated internally by the list operations).
Crate structure
At a high level, the crate defines:
Where SequenceNumber is a type alias provided elsewhere in the bitcoinleveldb stack (typically u64).
SnapshotImpl implements Snapshot, allowing it to be passed through the higher-level API as an opaque handle.
Usage
This crate is designed to be used by the database engine, not by application code directly. In practice, a DB will:
- Maintain a
SnapshotListinside the DB state. - On
DB::GetSnapshot(or equivalent), callSnapshotList::new(current_sequence_number)to allocate and link a new snapshot, and return it as a&dyn Snapshotto the caller. - On
DB::ReleaseSnapshot, cast the API-level snapshot reference back to the internalSnapshotImplpointer and callSnapshotList::delete.
Creating and managing snapshots
use ;
Be very careful to avoid use-after-free: after delete, the pointer is invalid.
Interoperating with higher-level APIs
The trait Snapshot is intentionally minimal:
SnapshotImpl implements Snapshot, enabling APIs like:
The public Snapshot interface is intentionally opaque: snapshot semantics (which sequence number, which files, etc.) are upheld entirely by the surrounding engine code. This crate focuses on ownership and ordering of those internal snapshot structures.
Design details
Heap-pinned sentinel
The SnapshotList stores a Box<SnapshotImpl> sentinel head. Its address is stable, even when the SnapshotList itself is moved in memory. This allows the list nodes to keep raw pointers to head without worrying about self-referential struct invalidation.
On Default construction:
- A
SnapshotImplwithsequence_number = 0is allocated and boxed. head.prevandhead.nextare set to point toheaditself.
The sentinel is never exposed as a public snapshot and must never be passed to delete.
Oldest and newest snapshots
The oldest and newest methods work as follows:
oldestreturnshead.next, i.e., the first real node after the sentinel.newestreturnshead.prev, i.e., the last real node before wrapping back to the sentinel.
Both methods assert that the list is non-empty in debug builds.
Insertion (new)
SnapshotList::new(sequence_number) performs:
- When the list is not empty, assert that the new sequence is
>=the current newest sequence. - Allocate a
SnapshotImplwith the given sequence. - In non-
NDEBUGbuilds, write itslistback-pointer. - Insert the new node just before
head, updating the adjacentprev/nextpointers accordingly.
Deletion (delete)
SnapshotList::delete(snapshot) performs:
- Return early if
snapshotis null. - Assert that
snapshotis not the sentinel head. - In non-
NDEBUGbuilds, assert that the snapshot'slistmatchesself. - Relink
prev.nextandnext.prevto bypass the node. - Clear the node's
prev,next, and (non-NDEBUG)listfields. drop(Box::from_raw(snapshot_mut));deallocates the node.
The caller must ensure that snapshot is a valid pointer to a node currently linked into this list, and that no further dereferences occur after deletion.
Concurrency
Snapshots as logical database views are immutable: higher-level code must treat Snapshot as read-only once published. The list itself, however, is not internally synchronized. The crate does not perform any locking; it assumes that the owner (DB or similar) enforces appropriate synchronization.
Typical patterns:
- Guard all snapshot list manipulations (
new,delete) with a mutex protecting the DB state. - Allow concurrent readers to use snapshots through immutable references once
DB::GetSnapshothas returned them.
Logging and diagnostics
The implementation uses macros such as debug!, trace!, and warn!. These are typically provided by the log crate or a similar facade. They offer insight into:
- Creation and deletion of snapshots
- Pointer topology and list structure changes
- Empty vs non-empty list checks
In performance-sensitive deployments you may choose a logging configuration that elides these at compile time.
Relationship to bitcoin-rs
This crate lives in the bitcoin-rs repository:
- Repository: https://github.com/klebs6/bitcoin-rs
It is one piece of a broader system that re-implements or adapts LevelDB for use as the backing store for Bitcoin-related data structures. The snapshot system is critical to providing consistent point-in-time reads while compactions, memtable flushes, and other background operations occur.
License
This crate is distributed under the MIT license. See the repository for details.