[−][src]Crate rkv
A simple, humane, typed key-value storage solution. It supports multiple backend engines with varying guarantees, such as LMDB for performance, or "SafeMode" for reliability.
It aims to achieve the following:
- Avoid sharp edges (e.g., obscure error codes for common situations).
- Report errors via failure.
- Correctly restrict access to one handle per process via a Manager.
- Use Rust's type system to make single-typed key stores safe and ergonomic.
- Encode and decode values via bincode/serde and type tags, achieving platform-independent storage and input/output flexibility.
It exposes these primary abstractions:
- Manager: a singleton that controls access to environments
- Rkv: an environment contains a set of key/value databases
- SingleStore: a database contains a set of key/value pairs
Keys can be anything that implements AsRef<[u8]>
or integers
(when accessing an IntegerStore).
Values can be any of the types defined by the Value enum, including:
- booleans (
Value::Bool
) - integers (
Value::I64
,Value::U64
) - floats (
Value::F64
) - strings (
Value::Str
) - blobs (
Value::Blob
)
See Value for the complete list of supported types.
Basic Usage
use rkv::{Manager, Rkv, SingleStore, Value, StoreOptions}; use rkv::backend::{Lmdb, LmdbEnvironment}; use std::fs; use tempfile::Builder; // First determine the path to the environment, which is represented on disk as a // directory containing two files: // // * a data file containing the key/value stores // * a lock file containing metadata about current transactions // // In this example, we use the `tempfile` crate to create the directory. // let root = Builder::new().prefix("simple-db").tempdir().unwrap(); fs::create_dir_all(root.path()).unwrap(); let path = root.path(); // The `Manager` enforces that each process opens the same environment at most once by // caching a handle to each environment that it opens. Use it to retrieve the handle // to an opened environment—or create one if it hasn't already been opened: let mut manager = Manager::<LmdbEnvironment>::singleton().write().unwrap(); let created_arc = manager.get_or_create(path, Rkv::new::<Lmdb>).unwrap(); let env = created_arc.read().unwrap(); // Then you can use the environment handle to get a handle to a datastore: let store = env.open_single("mydb", StoreOptions::create()).unwrap(); { // Use a write transaction to mutate the store via a `Writer`. There can be only // one writer for a given environment, so opening a second one will block until // the first completes. let mut writer = env.write().unwrap(); // Keys are `AsRef<[u8]>`, while values are `Value` enum instances. Use the `Blob` // variant to store arbitrary collections of bytes. Putting data returns a // `Result<(), StoreError>`, where StoreError is an enum identifying the reason // for a failure. store.put(&mut writer, "int", &Value::I64(1234)).unwrap(); store.put(&mut writer, "uint", &Value::U64(1234_u64)).unwrap(); store.put(&mut writer, "float", &Value::F64(1234.0.into())).unwrap(); store.put(&mut writer, "instant", &Value::Instant(1528318073700)).unwrap(); store.put(&mut writer, "boolean", &Value::Bool(true)).unwrap(); store.put(&mut writer, "string", &Value::Str("Héllo, wörld!")).unwrap(); store.put(&mut writer, "json", &Value::Json(r#"{"foo":"bar", "number": 1}"#)).unwrap(); store.put(&mut writer, "blob", &Value::Blob(b"blob")).unwrap(); // You must commit a write transaction before the writer goes out of scope, or the // transaction will abort and the data won't persist. writer.commit().unwrap(); } { // Use a read transaction to query the store via a `Reader`. There can be multiple // concurrent readers for a store, and readers never block on a writer nor other // readers. let reader = env.read().expect("reader"); // Keys are `AsRef<u8>`, and the return value is `Result<Option<Value>, StoreError>`. println!("Get int {:?}", store.get(&reader, "int").unwrap()); println!("Get uint {:?}", store.get(&reader, "uint").unwrap()); println!("Get float {:?}", store.get(&reader, "float").unwrap()); println!("Get instant {:?}", store.get(&reader, "instant").unwrap()); println!("Get boolean {:?}", store.get(&reader, "boolean").unwrap()); println!("Get string {:?}", store.get(&reader, "string").unwrap()); println!("Get json {:?}", store.get(&reader, "json").unwrap()); println!("Get blob {:?}", store.get(&reader, "blob").unwrap()); // Retrieving a non-existent value returns `Ok(None)`. println!("Get non-existent value {:?}", store.get(&reader, "non-existent").unwrap()); // A read transaction will automatically close once the reader goes out of scope, // so isn't necessary to close it explicitly, although you can do so by calling // `Reader.abort()`. } { // Aborting a write transaction rolls back the change(s). let mut writer = env.write().unwrap(); store.put(&mut writer, "foo", &Value::Str("bar")).unwrap(); writer.abort(); let reader = env.read().expect("reader"); println!("It should be None! ({:?})", store.get(&reader, "foo").unwrap()); } { // Explicitly aborting a transaction is not required unless an early abort is // desired, since both read and write transactions will implicitly be aborted once // they go out of scope. { let mut writer = env.write().unwrap(); store.put(&mut writer, "foo", &Value::Str("bar")).unwrap(); } let reader = env.read().expect("reader"); println!("It should be None! ({:?})", store.get(&reader, "foo").unwrap()); } { // Deleting a key/value pair also requires a write transaction. let mut writer = env.write().unwrap(); store.put(&mut writer, "foo", &Value::Str("bar")).unwrap(); store.put(&mut writer, "bar", &Value::Str("baz")).unwrap(); store.delete(&mut writer, "foo").unwrap(); // A write transaction also supports reading, and the version of the store that it // reads includes the changes it has made regardless of the commit state of that // transaction. // In the code above, "foo" and "bar" were put into the store, then "foo" was // deleted so only "bar" will return a result when the database is queried via the // writer. println!("It should be None! ({:?})", store.get(&writer, "foo").unwrap()); println!("Get bar ({:?})", store.get(&writer, "bar").unwrap()); // But a reader won't see that change until the write transaction is committed. { let reader = env.read().expect("reader"); println!("Get foo {:?}", store.get(&reader, "foo").unwrap()); println!("Get bar {:?}", store.get(&reader, "bar").unwrap()); } writer.commit().unwrap(); { let reader = env.read().expect("reader"); println!("It should be None! ({:?})", store.get(&reader, "foo").unwrap()); println!("Get bar {:?}", store.get(&reader, "bar").unwrap()); } // Committing a transaction consumes the writer, preventing you from reusing it by // failing at compile time with an error. This line would report "error[E0382]: // borrow of moved value: `writer`". // store.put(&mut writer, "baz", &Value::Str("buz")).unwrap(); } { // Clearing all the entries in the store with a write transaction. { let mut writer = env.write().unwrap(); store.put(&mut writer, "foo", &Value::Str("bar")).unwrap(); store.put(&mut writer, "bar", &Value::Str("baz")).unwrap(); writer.commit().unwrap(); } { let mut writer = env.write().unwrap(); store.clear(&mut writer).unwrap(); writer.commit().unwrap(); } { let reader = env.read().expect("reader"); println!("It should be None! ({:?})", store.get(&reader, "foo").unwrap()); println!("It should be None! ({:?})", store.get(&reader, "bar").unwrap()); } }
Re-exports
pub use migrator::Migrator; |
pub use store::single::SingleStore; |
pub use store::Options as StoreOptions; |
pub use value::OwnedValue; |
pub use value::Value; |
pub use store::multi::MultiStore; |
pub use store::integer::IntegerStore; |
pub use store::integermulti::MultiIntegerStore; |
Modules
backend | |
migrator | A simple utility for migrating data from one RVK environment to another. Notably, this tool can migrate data from an enviroment created with a different backend than the current RKV consumer (e.g from Lmdb to SafeMode). |
store | |
value |
Structs
Manager | A process is only permitted to have one open handle to each Rkv environment. This manager exists to enforce that constraint: don't open environments directly. |
Reader | |
Rkv | Wrapper around an |
Writer |
Enums
DataError | |
DatabaseFlags | |
EnvironmentFlags | |
MigrateError | |
StoreError | |
WriteFlags |
Traits
EncodableKey | |
PrimitiveInt | |
Readable |