Rust · Quickstart

Quickstart

use ustore::DataDirectory;use ustore::Database;use ustore::Engine;use ustore::LevelDBOptions;use ustore::NVMEOptions;use ustore::RAMOptions;use ustore::RocksDBOptions; 1.0.0-rc.1 · d6d40961

A Database owns its directory, and a Collection addresses one collection in it by name, as the Python binding does with db['movies'].

use ustore::{jsontape, Arena, Database, Durability, Edge, Key, RAMOptions, Role};

let database = Database::init_ram(&RAMOptions::default())?;
let [avatar, titanic] = [1u64, 2].map(Key::from);

database.collection("movies")?.docs().set(avatar, &jsontape::parse(br#"{"title":"Avatar","year":2009}"#)?)?;
database.collection("posters")?.blobs().set(avatar, &png_bytes)?;
database.collection("similar")?.graph().add_edge(avatar, titanic)?;

let mut movies = database.collection("movies")?;
let description = movies.docs().get(avatar)?.expect("written above");
assert_eq!(description["year"].as_u64(), Some(2009));

let mut posters = database.collection("posters")?;
let thumbnail: Option<Vec<u8>> = posters.blobs().get(avatar)?;

let mut similar = database.collection("similar")?;
let recommendations: Vec<Edge> = similar.graph().read_edges(&[avatar], Role::Source)?;

let mut arena = Arena::new()?;
let mut pictures = database.collection("pictures")?;
let images = pictures.sample(1000, &mut arena)?;
let tape = images.values().tape();

let mut transaction = database.transaction()?;
transaction.collection("movies")?.docs().set(titanic, &jsontape::parse(br#"{"title":"Titanic","year":1997}"#)?)?;
transaction.collection("similar")?.graph().add_edge(titanic, avatar)?;
transaction.collection("posters")?.blobs().set(titanic, &png_bytes)?;
transaction.commit(Durability::Buffered)?;

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.

§ Engines & Lifecycle

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 value
people.compact(None)?;            // reclaims what overwritten and removed data still takes
people.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.
db.rs:2326 ↗
pub struct DataDirectory<'path>

Fields

path&'path strThe directory, which must exist; on NVMe also a raw device from a base, like /dev/nvme0n1@64GiB.
max_bytesu64Bytes the files may take there: zero is unlimited on RocksDB and the whole medium on NVMe.

Implements

Clone, Copy, StructuralPartialEq, PartialEq, Eq, Debug, Default, Hash
StructDatabaseAn open embedded database, which owns its directory until it is dropped.17
db.rs:2534 ↗
pub struct Database

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.
db.rs:86 ↗
pub fn collection(&self, name: &str) -> Result<Collection<'_>>
init_ramStarts an empty database of the in-memory engine, which persists nowhere.
db.rs:2547 ↗
pub fn init_ram(options: &RAMOptions<'_>) -> Result<Self>

Calls ustore_database_init_ram.

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.
db.rs:2570 ↗
pub fn open_ram(directory: &str, options: &RAMOptions<'_>) -> Result<Self>

Calls ustore_database_open_ram.

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.
db.rs:2594 ↗
pub fn open_rocksdb(options: &RocksDBOptions<'_>) -> Result<Self>

Calls ustore_database_open_rocksdb.

view_rocksdbServes an existing directory in place through the RocksDB engine, read-only, or fails with Status::Unsupported in a build without the engine.
db.rs:2606 ↗
pub fn view_rocksdb(options: &RocksDBOptions<'_>) -> Result<Self>

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.
db.rs:2639 ↗
pub fn open_leveldb(options: &LevelDBOptions<'_>) -> Result<Self>

Calls ustore_database_open_leveldb.

open_nvmeServes the database a manifest names through the NVMe engine, or fails with Status::Unsupported in a build without it.
db.rs:2661 ↗
pub fn open_nvme(options: &NVMEOptions<'_>) -> Result<Self>

Calls ustore_database_open_nvme.

describeThe engine behind this database, what it supports and how much it holds.
db.rs:2705 ↗
pub fn describe(&self) -> Result<Description>

Calls ustore_database_describe.

clearEmpties every collection at once at the head; see On::clear.
db.rs:2738 ↗
pub fn clear(&self, mode: DropMode) -> Result<()>
compactReclaims the space overwritten and removed data still takes, one collection at a time; see On::compact.
db.rs:2744 ↗
pub fn compact<'config>(&self, config: impl Into<CompactConfig<'config>>) -> Result<()>
flushMakes every commit that returned before the call durable; see On::flush.
db.rs:2749 ↗
pub fn flush(&self) -> Result<()>
transactionBegins a transaction over this database, which conflicts rather than blocks.
db.rs:2767 ↗
pub fn transaction(&self) -> Result<Transaction<'_>>
snapshotPins a point-in-time view of the database, which it releases when dropped.
db.rs:2790 ↗
pub fn snapshot(&self) -> Result<Snapshot<'_>>
collection_createCreates an empty collection holding modality and binds name to it, refusing a name already in use.
db.rs:2818 ↗
pub fn collection_create(&self, name: &str, modality: Modality) -> Result<CollectionID>

Calls ustore_collection_create.

collection_dropRemoves the contents of a collection, or the collection itself together with its name.
db.rs:2823 ↗
pub fn collection_drop(&self, collection: CollectionID, mode: DropMode) -> Result<()>
changes_cursorOpens a feed of the changes committed to collection after since, oldest first, reading nothing until its first pull.
db.rs:4468 ↗
pub fn changes_cursor(
    &self,
    collection: CollectionID,
    config: impl Into<ChangesCursorConfig>,
) -> Result<ChangesCursor<'_>>

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.
db.rs:2280 ↗
pub enum Engine

Values

RAM0The in-memory engine of Database::init_ram and Database::open_ram.
RocksDB1The RocksDB engine of Database::open_rocksdb and Database::view_rocksdb.
LevelDB2The LevelDB engine of Database::open_leveldb.
NVME3The NVMe engine of Database::open_nvme.

Implements

Clone, Copy, StructuralPartialEq, PartialEq, Eq, Debug, Hash, Display, FromStr
StructLevelDBOptionsWhat Database::open_leveldb opens.1
db.rs:2369 ↗
pub struct LevelDBOptions<'options>

Fields

directory&'options strThe database's directory, which must exist.
executorOption<&'options Executor>The executor the open runs on, after the calls submitted there before it; None opens on the calling thread.

Implements

Clone, Copy, Debug, Default
atA database in an existing directory.
db.rs:2378 ↗
pub fn at(directory: &'options str) -> Self
StructNVMEOptionsWhat Database::open_nvme opens, where every zero size or count keeps the engine's default.1
db.rs:2388 ↗
pub struct NVMEOptions<'path>

Fields

manifest&'path strThe manifest, <database>.ustore.json in an existing directory, or the raw address of the first slot.
data_directories&'path [DataDirectory<'path>]Further data directories the runs land on, the one with the most free bytes taking each.
executorOption<&'path Executor>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.
compactionsOption<&'path Executor>The executor of Executor::for_vram whose device and scratch limit every merge takes, read once at the open; None merges on host threads.
memory_limitu64Bytes of host memory the engine may pin and cache.
transaction_max_bytesu64Bytes a transaction may buffer before it must commit.
transaction_max_deltasusizeDeltas a transaction may buffer before it must commit.
collection_concurrency_limitusizeOperations that may run on one collection at once.
collection_cache_limitu64Bytes of values each collection may cache.
collection_memory_runs_max_bytesu64Bytes of memory runs a collection holds before the commit that crosses them flushes level 0.
collection_memory_runs_max_deltasusizeDeltas of memory runs a collection holds before the commit that crosses them flushes level 0.
collection_level_enlarge_factorusizeHow many times larger each level of a collection is than the one above it.
collection_value_sizeu64Bytes of every value of a collection whose values share one size, or zero for varying sizes.
collection_value_max_sizeu64Bytes of the largest value of a collection whose values vary in size.
cpu_classOption<CPUClass>The CPU class the database confines itself to, failing with Status::Unsupported where the CPU does not run it; None takes what the CPU offers.

Implements

Clone, Copy, Debug, Default
atThe database a manifest names, with every default.
db.rs:2428 ↗
pub fn at(manifest: &'path str) -> Self
StructRAMOptionsHow Database::init_ram and Database::open_ram start the in-memory engine.
db.rs:2336 ↗
pub struct RAMOptions<'options>

Fields

isolationIsolationHow transactions see each other.
executorOption<&'options Executor>The executor the open runs on, after the calls submitted there before it; None opens on the calling thread.
cpu_classOption<CPUClass>The CPU class the database confines itself to, failing with Status::Unsupported where the CPU does not run it; None takes what the CPU offers.

Implements

Clone, Copy, Debug, Default
StructRocksDBOptionsWhat Database::open_rocksdb and Database::view_rocksdb open.1
db.rs:2348 ↗
pub struct RocksDBOptions<'options>

Fields

directory&'options strThe database's directory, which must exist.
data_directories&'options [DataDirectory<'options>]Directories the table files fill in order, or none to keep them in directory.
executorOption<&'options Executor>The executor the open runs on, after the calls submitted there before it; None opens on the calling thread.

Implements

Clone, Copy, Debug, Default
atA database in an existing directory.
db.rs:2359 ↗
pub fn at(directory: &'options str) -> Self