Reads and snapshots
Point reads, many-key reads, paged scans, many-range scans and bounded iterators — each at one consistent snapshot — plus held snapshots and the read options.
Every read sees one snapshot: the database as it was at one sequence number. A read never sees half of a batch, and a read that spans many keys sees them all at the same moment.
One key
use tessarikv::{Db, KeyspaceId, ReadOptions};
async fn stock(db: &Db) -> tessarikv::Result<Option<Vec<u8>>> {
db.get(KeyspaceId::DEFAULT, b"sku:1001", &ReadOptions::default()).await
}
get returns None for a key that is absent or deleted.
Many keys
multi_get reads many keys at one snapshot and returns the values in the order
the keys were given.
use tessarikv::{Db, KeyspaceId, ReadOptions};
async fn stock_of_three(db: &Db) -> tessarikv::Result<Vec<Option<Vec<u8>>>> {
let keys: [&[u8]; 3] = [b"sku:1001", b"sku:1002", b"sku:1003"];
db.multi_get(KeyspaceId::DEFAULT, keys, &ReadOptions::default()).await
}
A range, page by page
scan(keyspace, start, end, limit, options) returns a ScanPage: up to limit
pairs, ascending by key, from start up to but not including end (None
reaches the end of the keyspace). When the page stopped at its limit,
page.next is the key the next page starts at.
use tessarikv::{Db, KeyspaceId, ReadOptions};
async fn every_sku(db: &Db) -> tessarikv::Result<usize> {
let options = ReadOptions::default();
let mut start = b"sku:".to_vec();
let mut seen = 0;
loop {
let page = db.scan(KeyspaceId::DEFAULT, &start, Some(b"sku;"), 100, &options).await?;
seen += page.entries.len();
match page.next {
Some(next) => start = next,
None => return Ok(seen),
}
}
}
Each page is read at its own snapshot. To read several pages at one moment, take a snapshot and scan through it.
multi_scan reads several ranges, each a ScanRange { start, end, limit }, at
one snapshot and returns one page per range.
Iterators
Db::iter(keyspace, options) returns a DbIterator positioned nowhere. Seek
first, then step:
seek_to_first,seek_to_last,seek(key)(first key at or afterkey),seek_for_prev(key)(last key at or beforekey);next,prev;valid,key,value.
use tessarikv::{Db, KeyspaceId, ReadOptions};
async fn users(db: &Db) -> tessarikv::Result<Vec<Vec<u8>>> {
// Bounds the iterator to the keys that start with "user/".
let options = ReadOptions::default().with_prefix(b"user/");
let mut iterator = db.iter(KeyspaceId::DEFAULT, &options)?;
let mut keys = Vec::new();
iterator.seek_to_first().await?;
while iterator.valid() {
keys.push(iterator.key().to_vec());
iterator.next().await?;
}
Ok(keys)
}
An iterator holds its snapshot for as long as it lives.
Snapshots
Db::snapshot() returns a Snapshot that keeps one moment readable. It offers
the same reads as the database: get, multi_get, scan, multi_scan and
iter. sequence() is the sequence number it reads at.
use tessarikv::{Db, KeyspaceId, ReadOptions};
async fn consistent_pair(db: &Db) -> tessarikv::Result<(Option<Vec<u8>>, Option<Vec<u8>>)> {
let snapshot = db.snapshot()?;
let options = ReadOptions::default();
let order = snapshot.get(KeyspaceId::DEFAULT, b"order:9", &options).await?;
let stock = snapshot.get(KeyspaceId::DEFAULT, b"sku:1001", &options).await?;
Ok((order, stock))
}
The snapshot is released when it is dropped. While it lives, compaction keeps
every version it can see, so a forgotten snapshot keeps old data on disk.
Db::oldest_snapshot() reports the sequence of the oldest one still held, by a
snapshot, an iterator or a transaction. Watch it as a metric; it walks every
registered snapshot, so do not call it per request.
Read options
| Field | Default | Effect |
|---|---|---|
verify_checksums | true | verify the checksum of every block read from a table; off, a corrupt block decodes to an error or to wrong data instead of a Corruption error |
fill_cache | true | keep the blocks this read loads in the block cache; a one-off scan sets it to false so it does not evict the working set |
lower_bound | None | the least key an iterator shows, inclusive |
upper_bound | None | the key an iterator stops before, exclusive |
The bounds apply to iterators only. Point reads and scans take their range as
arguments. with_prefix(prefix) sets both bounds to the keys that start with
prefix.
Size of a range
Db::approximate_size(keyspace, start, end) estimates the bytes a range takes
in table files, from the table indexes, without reading data blocks.