WeaveDocs
Lens

Snapshots and Sub Databases

Snapshots and sub-databases in Lens — typed sub-DBs with isolated keyspaces and consistent point-in-time snapshots.

Purpose

B-tree key/value database with batches, snapshots, sub-databases, iterators, cache, compaction, and metrics.

This page follows the real source shape for Lens and explains the workflow a developer is likely to use first.

Developer workflow

Start from the smallest constructor or builder, perform one meaningful operation, inspect the returned state, then add the relevant policy, storage, or network integration. The examples below should be expanded whenever the crate API changes.

Note

Snapshots hold a read view at a specific generation. They keep older blocks alive on disk, so long-lived snapshots block compaction. Drop snapshots promptly to allow GC.

Primary types to know

  • AllowAllLensPolicy — models/lens/src/lens_policy.rs
  • Batch — models/lens/src/batch.rs
  • BlockCache — models/lens/src/block.rs
  • BlockPointer — models/lens/src/node.rs
  • CacheStats — models/lens/src/block.rs
  • CompactionOptions — models/lens/src/config.rs
  • Key — models/lens/src/node.rs
  • Lens — models/lens/src/lens.rs
  • LensConfig — models/lens/src/config.rs
  • LensMetrics — models/lens/src/metrics.rs
  • LensMetricsSnapshot — models/lens/src/metrics.rs
  • Metadata — models/lens/src/lens.rs

Example shape

use lens::{Lens, LensConfig, SubOptions};
use std::sync::Arc;
use strand::{Strand, StrandConfig};
use tokio::sync::RwLock as AsyncRwLock;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let dir = tempfile::tempdir()?;
    let strand = Strand::new(StrandConfig::new().with_storage(dir.path())).await?;
    let lens = Lens::new(Arc::new(AsyncRwLock::new(strand)), LensConfig::default()).await?;

    // A SubDatabase is a prefix-isolated view onto the same physical tree.
    // Useful for tenant or namespace isolation without opening a new strand.
    let users = lens.sub_with_options("users", SubOptions::default());
    users.put(b"alice", b"admin").await?;
    users.put(b"bob", b"reader").await?;

    // Snapshot the current tree root so future writes don't change what we observe.
    let snapshot_id = lens.snapshot_with_name("post-onboarding".to_string()).await?;

    // Mutate the live tree after snapshotting.
    users.put(b"carol", b"writer").await?;

    // The snapshot still resolves to the pre-mutation state.
    let snap = lens.get_snapshot(&snapshot_id).expect("snapshot exists");
    println!(
        "snapshot id={} entries={} strand_length={} valid_now={}",
        snap.id,
        snap.metadata.entries,
        snap.strand_length,
        snap.is_valid_at(snap.strand_length),
    );

    lens.delete_snapshot(&snapshot_id)?;
    Ok(())
}