Struct libriakv::RiaKV [−][src]
Expand description
Generic representation of a key value store in libriakv
The underlying storage can be any type with the Read + Write + Seek
trait bounds.
The index
attribute is used to maintain a mapping from keys to the
position in the underlying storage where their corresponding entries are stored.
Fields
f: F
underlying storage
index: HashMap<ByteString, u64>
index - storing a mapping from keys to the position where the key value entry is stored
Implementations
Creates a new RiaKV
instance from a file stored at the given path as th
backing store.
Example
use libriakv::RiaKV; let storage_path = std::path::Path::new("/path/to/some/file.db"); match RiaKV::open_from_file_at_path(storage_path) { Ok(opened_store) => {}, // use the opened store _ => {} // handle failure };
Processes a record from the current position in the underlying storage file. Every record (key value pair) is stored with the following layout:
┌────────────────┬────────────┬──────────────┬────────────────┐ │ crc32 checksum │ key length │ value length │ KeyValuePair{} │ └────────────────┴────────────┴──────────────┴────────────────┘
Reading a record from the underlying storage occurs in the following steps:
- Read the checksum, key length and value length as 32 bit integers with little endian format
- Read the next (key length + value length) bytes into a bytestring
- Verify that the crc32 checksum of the data Bytestring read matches with the crc32 checksum read
- Split off the bytestring at key length from the start to obtain the key and the value
- Return
KeyValuePair { key, value }
Example
use std::io; use libriakv::RiaKV; let mut cursor = io::Cursor::new(vec![0; 5000]); // .. enter some data into the cursor let maybe_kv = RiaKV::<io::Cursor<Vec<u8>>>::process_record(&mut cursor);
Seeks to the end of the underlying storage file. Any subsequent read should end in EOF
.
pub fn for_each_kv_entry_in_storage<Func>(
&mut self,
callback: Func
) -> Result<()> where
Func: FnMut(KeyValuePair, u64) -> IndexOp,
pub fn for_each_kv_entry_in_storage<Func>(
&mut self,
callback: Func
) -> Result<()> where
Func: FnMut(KeyValuePair, u64) -> IndexOp,
For each function for processing all KeyValuePair{}
instances stored in the underlying
storage.
The key value entries are processed in the following way:
- First we backup the current position of the underlying storage since it would otherwise be lost during scanning the entire storage file
- Next we seek to the start of the storage file
- Now in an infinite loop, during every iteration
- We seek to the current position
- We read a record using
RiaKV::process_record
- If the record read is not an error, we operate on it using the callback
- In the case of an error
- For simple EOF we break out of the loop
- In the case of any other error, we return
Err(err)
- Now if the callback is executed, the return value is used as follows:
- For
IndexOp::Insert
the key value pair is inserted into the index - For
IndexOp::Delete
the key value pair is deleted from the index if it existed in the index before - For
Index::Nop
we do nothing a continue to the next iteration - For
Index::End
we break out of the loop
- For
- When we exit from the loop, we seek back to the position we saved before entering into the loop
- We return
Ok(())
Example
use libriakv::{RiaKV, IndexOp, ByteString, ByteStr}; use std::io; use std::io::prelude::*; // As used in the impl{} of RiaKV itself fn load<F>(store: &mut RiaKV<F>) -> io::Result<()> where F: Read + Write + Seek { store.for_each_kv_entry_in_storage(|kv, position| { if kv.value.len() > 0 { IndexOp::Insert(kv, position) } else { IndexOp::Delete(kv, position) } }) } // ... fn find<F>(store: &mut RiaKV<F>, target: &ByteStr) -> io::Result<Option<(u64, ByteString)>> where F: Read + Write + Seek, { let mut found: Option<(u64, ByteString)> = None; store.for_each_kv_entry_in_storage(|kv, position| { if kv.key == target.to_vec() { found = Some((position, kv.value)); IndexOp::End } else { IndexOp::Nop } })?; Ok(found) }
Loads all the key value entries from the underlying storage
Gets the KeyValuePair{}
instance stored at the given position in the
underlying storage.
Gets the value for the given key.
Example
use libriakv::RiaKV; let mut store = RiaKV::open_from_in_memory_buffer(5000); store.insert(b"key", b"value").expect("insert"); store.get(b"key").expect("get").unwrap();
Finds the first KeyValueEntry{}
corresponding to the given ByteStr
key.
Note: Since this implementation is an append only, log structured store, deleted entries will always have corresponding entries.
Example
use libriakv::RiaKV; let mut store = RiaKV::open_from_in_memory_buffer(5000); store.insert(b"key", b"value").expect("insert"); store.find(b"key").expect("find").unwrap();
Inserts the given key value pair into the underlying storage and returns the position in the underlying storage file, it was written at. The index is not updated.
As mentioned before, the following layout is used for storing the key value pair:
┌────────────────┬────────────┬──────────────┬────────────────┐ │ crc32 checksum │ key length │ value length │ KeyValuePair{} │ └────────────────┴────────────┴──────────────┴────────────────┘
This method is intended to be used in the actual RiaKV::insert()
implementation.
Inserts the given key value pair into the underlying storage and updates the index.
Example
use libriakv::RiaKV; let mut store = RiaKV::open_from_in_memory_buffer(5000); store.insert(b"key", b"value").expect("insert");
Updates the value for the given key by inserting a duplicate entry into the storage and updating the index.
Loads the index from the given object implementing the Read
trait.
This done by deserializing the contents of the file using
bincode::deserialize_from(reader)
into a HashMap<ByteString, u64>
.
The Read
object is wrapped into a io::BufReader
instance before
reading the contents.