Trait matterdb::Database [−][src]
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.