A collection resolves its name through its view once, and every verb goes through a view of one modality: blobs, docs or graph.
The first write through a view creates a missing collection holding that view’s modality, a read of a missing one fails with Status::NotFound, and a view of another modality fails with Status::InvalidArgument. sample draws keys at random and reads their values through the same view, so a sample holds both.
A collection keeps no arena.
A call with no arena to pass, such as get, contains, the document reads and the graph lookups, makes a one-off arena through ustore_arena_init, copies what it read out of it, and frees it with the call: slower than reusing an arena, and stateless.
The batch reads, Blobs::read and Collection::sample, take the Arena they land in and answer views into it.
Writes land Durability::Buffered, and every call through a collection runs on the calling thread. Transaction::collection reads and writes inside the transaction, while its name resolves at the head.
Collections are created and dropped nowhere but at the head, so a first write from inside a transaction creates the collection there at once, and a name never joins the transaction’s conflicts.
Its collections borrow the transaction, so they are no longer used by the time Transaction::commit takes it mutably.
The constructor names the engine and what it does with a directory: init_ram starts an empty database in memory alone, open_<engine> serves a directory in place, and view_rocksdb serves one read-only.
An engine the build left out fails to open with Status::Unsupported.
use ustore::{DropMode, Engine, Isolation, LevelDBOptions, RAMOptions, RocksDBOptions};let scratch = Database::init_ram(&RAMOptions { isolation: Isolation::Snapshot, ..RAMOptions::default() })?;let imaged = Database::open_ram("/var/lib/imaged", &RAMOptions::default())?;let people = Database::open_rocksdb(&RocksDBOptions::at("/var/lib/people"))?;let replica = Database::view_rocksdb(&RocksDBOptions::at("/var/lib/replica"))?;let places = Database::open_leveldb(&LevelDBOptions::at("/var/lib/places"))?;let description = people.describe()?;assert_eq!(description.engine, Engine::RocksDB);println!("{} keys in {} bytes on disk", description.keys, description.disk_bytes);people.clear(DropMode::Values)?; // keeps every key, emptying its valuepeople.compact(None)?; // reclaims what overwritten and removed data still takespeople.snapshot()?.save("/var/lib/people-copy")?;
open_ram loads the image its directory holds, if any, and rewrites it whole on a flushed commit and on close.
A database view_rocksdb opened reads the state its directory held at open and refuses every call that would change it with Status::ReadOnly: writes, removes, transactions, collection creates and drops, clears and compactions, while snapshots and their saves still serve. describe answers the engine, the isolation transactions see, the keys across every collection, and the bytes the data takes in memory and on disk, where every size is the engine’s estimate and zero where it keeps none.
clear empties every collection at once, as collection_drop empties one: DropMode::Pairs keeps the collections and their names, DropMode::Collection drops every collection with its name, and DropMode::Values keeps the keys where the engine can.
The RAM and RocksDB engines refuse to drop the collections while a snapshot is open, with Status::InvalidArgument, since the snapshot belongs to its holder. compact reclaims space one collection at a time: RocksDB and LevelDB flush and compact their trees, and the RAM engine frees the versions no reader needs and rewrites its image. Snapshot::save writes the snapshot out as a new database of the same engine, holding exactly the collections and names the snapshot records, which that engine’s open then serves.
RocksDB and LevelDB create the path, while the RAM engine writes its image into the existing directory the path names. clear and compact are verbs of the head, and save of a snapshot, so each runs on an executor or defers as every other verb does.
API
StructDataDirectoryA directory the data files may occupy, with the bytes they may take there.
No two processes may open one directory at once, and the handle is shared across threads freely;
the transactions taken from it are not. Dropping it waits for every deferred call naming it to finish.
Implements
Debug, Send, Sync, Drop
collectionThe collection name addresses at the head, resolved now and created by its first write.
open_ramServes an existing directory in place through the in-memory engine, loading the image it holds, if any, and rewriting it whole on a flushed commit and on close.
open_rocksdbServes a directory in place through the RocksDB engine, creating the database when it holds none, or fails with Status::Unsupported in a build without the engine.
view_rocksdbServes an existing directory in place through the RocksDB engine, read-only, or fails with Status::Unsupported in a build without the engine.
The database reads the state its directory held at open, and refuses every call that would change it with
[Status::ReadOnly]: writes, removes, transactions, collection creates and drops, clears and compactions.
Snapshots and their saves still serve.
Calls ustore_database_view_rocksdb.
open_leveldbServes a directory in place through the LevelDB engine, creating the database when it holds none, or fails with Status::Unsupported in a build without the engine.
A feed whose history is already gone is refused with Status::WriteConflict, and an
engine that keeps no stamps, as LevelDB does, refuses it with Status::Unsupported.
EnumEngineThe engine serving a database, as ustore_engine_t spells it.
The executor the open runs on, after the calls submitted there before it; None opens on the calling thread.
The database moves bytes as one of Executor::for_nvme says, read once at the open, and with that call's
defaults for any other executor and for none.