Trait matterdb::Database[][src]

pub trait Database: Send + Sync + 'static {
    fn snapshot(&self) -> Box<dyn Snapshot>;
fn merge(&self, patch: Patch) -> Result<()>;
fn merge_sync(&self, patch: Patch) -> Result<()>; fn fork(&self) -> Fork { ... } }

Low-level storage backend implementing a collection of named key-value stores (aka column families).

A Database instance is shared across different threads, so it must be Sync and Send.

There is no way to directly interact with data in the database; use snapshot, fork and merge methods for indirect interaction. See the crate-level documentation for more details.

Note that Database effectively has interior mutability; merge and merge_sync methods take a shared reference to the database (&self) rather than an exclusive one (&mut self). This means that the following code compiles:

use matterdb::{access::CopyAccessExt, Database, TemporaryDB};

// not declared as `mut db`!
let db: Box<dyn Database> = Box::new(TemporaryDB::new());
let fork = db.fork();
{
    let mut list = fork.get_list("list");
    list.push(42_u64);
}
db.merge(fork.into_patch()).unwrap();

Merge Workflow

The user of a Database is responsible to ensure that forks are either created and merged sequentially or do not contain overlapping changes. By sequential creation we mean the following workflow:

let db = TemporaryDB::new();
let first_fork = db.fork();
// Perform some operations on `first_fork`...
db.merge(first_fork.into_patch()).unwrap();
let second_fork = db.fork();
// Perform some operations on `second_fork`...
db.merge(second_fork.into_patch()).unwrap();

In contrast, this is a non-sequential workflow:

let db = TemporaryDB::new();
let first_fork = db.fork();
// Perform some operations on `first_fork`...
let second_fork = db.fork();
// Perform some operations on `second_fork`...
db.merge(first_fork.into_patch()).unwrap();
db.merge(second_fork.into_patch()).unwrap();

In a non-sequential workflow, first_fork and second_fork must not contain overlapping changes (i.e., changes to the same index). If they do, the result of the merge may be unpredictable to the programmer and may break database invariants, e.g., that the length of an index is equal to the number of elements obtained by iterating over the index:

// NEVER USE THIS PATTERN!
let db = TemporaryDB::new();
let first_fork = db.fork();
first_fork.get_list("list").extend(vec![1, 2, 3]);
let second_fork = db.fork();
second_fork.get_list("list").push(4);
db.merge(first_fork.into_patch()).unwrap();
db.merge(second_fork.into_patch()).unwrap();

let snapshot = db.snapshot();
let list = snapshot.get_list::<_, i32>("list");
assert_eq!(list.len(), 1);
assert_eq!(list.iter().collect::<Vec<_>>(), vec![4, 2, 3]);
// ^-- Oops, we got two phantom elements!

It is advised to create / merge patches sequentially whenever possible. The concurrent workflow should only be used for minor changes, for which the proof that a patch does not overlap with concurrent patches is tractable.

Required methods

fn snapshot(&self) -> Box<dyn Snapshot>[src]

Creates a new snapshot of the database from its current state.

fn merge(&self, patch: Patch) -> Result<()>[src]

Atomically applies a sequence of patch changes to the database.

Note that this method may be called concurrently from different threads, the onus to guarantee atomicity is on the implementor of the trait.

Logical Safety

Merging several patches which are not created sequentially and contain overlapping changes may result in the unexpected storage state and lead to the hard-to debug errors, storage leaks etc. See the trait docs for more details.

Errors

If this method encounters any form of I/O or other error during merging, an error variant will be returned. In case of an error, the method guarantees no changes are applied to the database.

fn merge_sync(&self, patch: Patch) -> Result<()>[src]

Atomically applies a sequence of patch changes to the database with fsync.

Note that this method may be called concurrently from different threads, the onus to guarantee atomicity is on the implementor of the trait.

Logical Safety

Merging several patches which are not created sequentially and contain overlapping changes may result in the unexpected storage state and lead to the hard-to debug errors, storage leaks etc. See the trait docs for more details.

Errors

If this method encounters any form of I/O or other error during merging, an error variant will be returned. In case of an error, the method guarantees no changes are applied to the database.

Loading content...

Provided methods

fn fork(&self) -> Fork[src]

Creates a new fork of the database from its current state.

Loading content...

Trait Implementations

impl Debug for dyn Database[src]

fn fmt(&self, f: &mut Formatter<'_>) -> Result[src]

Formats the value using the given formatter. Read more

Implementors

impl Database for RocksDB[src]

fn snapshot(&self) -> Box<dyn Snapshot>[src]

fn merge(&self, patch: Patch) -> Result<()>[src]

fn merge_sync(&self, patch: Patch) -> Result<()>[src]

impl Database for TemporaryDB[src]

fn snapshot(&self) -> Box<dyn Snapshot>[src]

fn merge(&self, patch: Patch) -> Result<()>[src]

fn merge_sync(&self, patch: Patch) -> Result<()>[src]

Loading content...