Readers
DbReader is SlateDB’s read-only handle. It implements the same DbRead operations as Db, but it does not own a mutable memtable and it does not write WAL records. It reads from a checkpointed manifest, the SSTs referenced by that manifest. When WAL replay is enabled, it also updates reader-local immutable memtables from newer WAL SSTs.
Opening
Section titled “Opening”This example opens a database, flushes one key to object storage, then opens a DbReader and reads that key back.
use slatedb::{Db, DbReader, Error};use slatedb::object_store::{memory::InMemory, ObjectStore};use std::sync::Arc;
#[tokio::main]async fn main() -> Result<(), Error> { let object_store: Arc<dyn ObjectStore> = Arc::new(InMemory::new());
let db = Db::open("example", Arc::clone(&object_store)).await?; db.put(b"key", b"value").await?; db.flush().await?;
let reader = DbReader::builder("example", object_store).build().await?; assert_eq!(reader.get(b"key").await?, Some("value".into())); Ok(())}Without DbReaderBuilder::with_checkpoint_id, SlateDB creates a checkpoint in the latest manifest and sets its lifetime from DbReaderOptions::checkpoint_lifetime. That checkpoint pins the initial L0 and sorted-run view. The reader then replays newer WAL SSTs into immutable memtables unless DbReaderOptions::skip_wal_replay is true.
With an explicit checkpoint ID, SlateDB opens exactly the manifest pinned by that checkpoint. It does not create a new checkpoint, refresh the user-managed checkpoint, delete it on close, or follow newer manifests or WAL SSTs.
When skip_wal_replay is true, the reader only sees data already referenced by the manifest it opened. If the reader owns the checkpoint, it can still pick up later memtable flushes and compaction results when the manifest changes, but it ignores WAL-only data between those manifest updates.
Internally, the reader tracks the pinned checkpoint and manifest, a deque of immutable memtables built from WAL replay, the last WAL file it replayed, and the highest durable sequence number it can expose. Reads go through the same internal Reader implementation that backs ordinary Db reads.
A DbReader has no mutable memtable of its own. Its precedence is replayed immutable memtables, then L0 SSTs, then sorted runs. When SlateDB replaces the reader’s checkpoint with a newer manifest, it drops WAL-replayed rows whose sequence numbers are now at or below the new manifest’s last_l0_seq, because L0 or lower levels already cover them.
Refresh Loop
Section titled “Refresh Loop”When the reader owns the checkpoint, it starts a background poller that wakes up every DbReaderOptions::manifest_poll_interval.
On each tick, SlateDB loads the latest manifest. If the visible SST layout changed, or if the latest L0 sequence number moved past the WAL state already replayed into the reader, it replaces the checkpoint and rebuilds state from the new manifest. Otherwise it keeps the current checkpoint and only looks for newer WAL SSTs.
SlateDB refreshes the checkpoint expiration after half of the configured lifetime has elapsed. DbReader::close stops the poller and deletes the checkpoint the reader created for itself.
Read Semantics
Section titled “Read Semantics”get, get_key_value, scan, and scan_prefix use the normal SST iterator stack. Options such as ReadOptions::cache_blocks, ScanOptions::read_ahead_bytes, and ScanOptions::max_fetch_tasks still control caching and scan behavior.
Visibility is narrower than Db. A reader does not share a writer’s live mutable memtable, so read options cannot expose unflushed writer state. DbReader only returns committed data that is already durable in object storage, either through the pinned manifest or through WAL SSTs it replayed itself.
Reader-side block and object-store caches use the same configuration surface as Db. Caching covers those layers.