Struct lsmlite_rs::LsmDb
source · pub struct LsmDb { /* private fields */ }
Expand description
This represents the main-memory handle to a database file, most operations on the database are performed through the corresponding handle, like writing to it, or opening cursors, or handling explicit transactions.
Implementations§
source§impl LsmDb
impl LsmDb
Additional to implementing Disk
, the following helper functions are also available.
sourcepub fn is_initialized(&self) -> bool
pub fn is_initialized(&self) -> bool
This function tests whether a database handle has been initialized.
sourcepub fn is_connected(&self) -> bool
pub fn is_connected(&self) -> bool
This function tests whether a database handle is connected.
sourcepub fn get_full_db_path(&self) -> Result<String, LsmErrorCode>
pub fn get_full_db_path(&self) -> Result<String, LsmErrorCode>
This function outputs the full-qualified path of the database. It errors if the database has not been initialized.
sourcepub fn get_compression_id(&self) -> Result<LsmCompressionLib, LsmErrorCode>
pub fn get_compression_id(&self) -> Result<LsmCompressionLib, LsmErrorCode>
This function outputs the compression id of the database. The only possible
error is LsmErrorCode::LsmMismatch
which means that records of the database
have been compressed with a unknown library. At this point there is not much
to do, and this error should be considered unrecoverable. That is, the database
can be considered corrupted, and its data is most probably lost.
Trait Implementations§
source§impl Default for LsmDb
impl Default for LsmDb
A default database. This database is not useful without
getting first initialized using Disk::initialize
. The purpose
of this method is to simply zero all attributes of LsmDb
.
source§impl Disk for LsmDb
impl Disk for LsmDb
source§fn initialize(&mut self, conf: DbConf) -> Result<(), LsmErrorCode>
fn initialize(&mut self, conf: DbConf) -> Result<(), LsmErrorCode>
This function sets up general variables about the database. Initializing
a handle more than once is considered LsmErrorCode::LsmMisuse
.
Example
use lsmlite_rs::*;
let db_conf = DbConf::new(
"/tmp/",
"my_db_b".to_string(),
);
let mut db: LsmDb = Default::default();
assert!(!db.is_initialized());
let rc = db.initialize(db_conf.clone());
assert_eq!(rc, Ok(()));
assert!(db.is_initialized());
let rc = db.initialize(db_conf);
assert_eq!(rc, Err(LsmErrorCode::LsmMisuse));
source§fn connect(&mut self) -> Result<(), LsmErrorCode>
fn connect(&mut self) -> Result<(), LsmErrorCode>
This method produces a main-memory handle to connect to the database. At this point the database file is created at the given path, and upon success, the database can be operated using any other available method for it.
Connecting to a database using the same handle more than once, or connecting using
an uninitialized handle, is considered LsmErrorCode::LsmMisuse
.
Example
use lsmlite_rs::*;
let db_conf = DbConf::new(
"/tmp/",
"my_db_c".to_string(),
);
let mut db: LsmDb = Default::default();
let rc = db.connect();
assert_eq!(rc, Err(LsmErrorCode::LsmMisuse));
let rc = db.initialize(db_conf);
let rc = db.connect();
assert_eq!(rc, Ok(()));
assert!(db.is_connected());
let rc = db.connect();
assert_eq!(rc, Err(LsmErrorCode::LsmMisuse));
source§fn disconnect(&mut self) -> Result<(), LsmErrorCode>
fn disconnect(&mut self) -> Result<(), LsmErrorCode>
This method frees up all the resources used by the main-memory handle. A call
to connect
has to have the corresponding call to disconnect
, otherwise:
- The resources that belong to the memory handle will be leaked.
- The database (file) won’t be closed and the next time we open it the recovery process will kick-in (which can take considerable time. Depending on the size of the log).
For completeness, LsmDb
also implements Drop
so that a handle
gets automatically released once it goes out of scope.
Disconnecting using an uninitialized handle, or a handle that is not yet connected is not considered an error.
Example
use lsmlite_rs::*;
let db_conf = DbConf::new(
"/tmp/",
"my_db_d".to_string(),
);
let mut db: LsmDb = Default::default();
let rc = db.disconnect();
assert_eq!(rc, Ok(()));
let rc = db.initialize(db_conf);
let rc = db.connect();
let rc = db.disconnect();
assert_eq!(rc, Ok(()));
assert!(!db.is_connected());
source§fn persist(&mut self, key: &[u8], value: &[u8]) -> Result<(), LsmErrorCode>
fn persist(&mut self, key: &[u8], value: &[u8]) -> Result<(), LsmErrorCode>
This function writes the given entry on the database file in a transactional manner. That is, it either writes it completely, or not, but it leaves the database in no inconsistent state.
Trying to persist data using an uninitialized handle, or one that is not yet
connected to a database, is considered LsmErrorCode::LsmMisuse
.
Example
use lsmlite_rs::*;
use rand::{thread_rng, Rng};
let db_conf = DbConf::new(
"/tmp/",
"my_db_e".to_string(),
);
let mut db: LsmDb = Default::default();
let rc = db.initialize(db_conf);
let rc = db.connect();
let mut prng = thread_rng();
// 16-byte random key (not very useful in practice).
let key: Vec<u8> = (0..16).map(|_| prng.gen_range(0..=255)).collect();
// 1 KB zeroed payload.
let value = vec![0; 1024];
let rc = db.persist(&key, &value);
assert_eq!(rc, Ok(()));
// This is also possible (would overwrite the entry)
let rc = Disk::persist(&mut db, &key, &value);
assert_eq!(rc, Ok(()));
let rc = db.disconnect();
source§fn update(&mut self, key: &[u8], value: &[u8]) -> Result<(), LsmErrorCode>
fn update(&mut self, key: &[u8], value: &[u8]) -> Result<(), LsmErrorCode>
This function is just sugar. The database file can be considered a primary index in which only one entry under a given key can exist. If another entry with an existing key is persisted, it overwrites the existing one.
source§fn delete(&mut self, key: &[u8]) -> Result<(), LsmErrorCode>
fn delete(&mut self, key: &[u8]) -> Result<(), LsmErrorCode>
This deletes the entry under the given key (in a transactional manner).
Trying to delete data using an uninitialized handle, or one that is not yet
connected to a database, is considered LsmErrorCode::LsmMisuse
.
Example
use lsmlite_rs::*;
use rand::{thread_rng, Rng};
let db_conf = DbConf::new(
"/tmp/",
"my_db_f".to_string(),
);
let mut db: LsmDb = Default::default();
let rc = db.initialize(db_conf);
let rc = db.connect();
let mut prng = thread_rng();
// 16-byte random key (not very useful in practice).
let key: Vec<u8> = (0..16).map(|_| prng.gen_range(0..=255)).collect();
// 1 KB zeroed payload.
let value = vec![0; 1024];
let rc = db.persist(&key, &value);
assert_eq!(rc, Ok(()));
// Entry under `key` will disappear.
let rc = db.delete(&key);
assert_eq!(rc, Ok(()));
let rc = db.disconnect();
source§fn delete_range(&mut self, begin: &[u8], end: &[u8]) -> Result<(), LsmErrorCode>
fn delete_range(&mut self, begin: &[u8], end: &[u8]) -> Result<(), LsmErrorCode>
This function deletes the open interval of keys (being, end) (in a transactional manner as well).
source§fn optimize(&mut self) -> Result<(), LsmErrorCode>
fn optimize(&mut self) -> Result<(), LsmErrorCode>
This function optimizes a database to make it occupy as little space as possible. Essentially, this function compacts the whole database into a single tightly-packed B-tree: Thus, read I/O is optimized. This function is thought to be used in an offline fashion - once all writers have finished with the database.
source§fn begin_transaction(&mut self) -> Result<(), LsmErrorCode>
fn begin_transaction(&mut self) -> Result<(), LsmErrorCode>
This function opens a transaction explicitly. All operations contained between
opening a transaction and committing it using Disk::commit_transaction
will be
performed atomically. Similarly, if the transaction is explicitly rolled back using
Disk::rollback_transaction
, all enclosed operations will not be persistent.
Observe that every database operation is contained in an implicit transaction. This
function is thought to encapsulate multiple operations into a single transaction.
source§fn commit_transaction(&mut self) -> Result<(), LsmErrorCode>
fn commit_transaction(&mut self) -> Result<(), LsmErrorCode>
This function commits an opened transaction. Without committing a transaction, all enclosed operations will remain hidden from the consistent state of the database.
source§fn rollback_transaction(&mut self) -> Result<(), LsmErrorCode>
fn rollback_transaction(&mut self) -> Result<(), LsmErrorCode>
This function rollbacks an opened transaction explicitly. All enclosed operations will remain hidden from the consistent state of the database.
source§fn cursor_open(&self) -> Result<LsmCursor<'_>, LsmErrorCode>
fn cursor_open(&self) -> Result<LsmCursor<'_>, LsmErrorCode>
This function returns a cursor to the underlying database.
This cursor can be operated by the methods provided by the Cursor
trait.
When opening a cursor, a snapshot of the database will be created for it. No
new data arriving after the cursor has been created will be visible to the
cursor. A cursor is used to performed read-only operations over the database.
That is, no data of the database can be modified through a cursor.
Trying to open a cursor using a uninitialized handle, or one that is not yet
connected to a database, is considered LsmErrorCode::LsmMisuse
.
Example
use lsmlite_rs::*;
let db_conf = DbConf::new(
"/tmp/",
"my_db_g".to_string(),
);
let mut db: LsmDb = Default::default();
let rc = db.initialize(db_conf);
let rc = db.connect();
// Opening a cursor for `db`. This cursor is currently
// not positioned anywhere, and thus no data can be extracted
// from it.
let cursor = db.cursor_open();
assert!(cursor.is_ok());
type C<'a> = LsmCursor<'a>
source§impl Drop for LsmDb
impl Drop for LsmDb
Drop for LsmDb
so that it gets properly terminated when it goes out of scope for example.
impl Send for LsmDb
A database handle is marked as Send
as it can be safely sent to another
thread (for further usage), for example in async code.
impl Sync for LsmDb
For convenience a database handle is marked as Sync
. This is not because the same
handle can be safely shared among threads, but because in this manner a handle can be
wrapped in a std::sync::RwLock
and be shared among threads safely as it captures
the single-writer, multiple-reader nature of a LsmDb
. In this manner, multiple
cursors may be opened through the same handle, while writes through the handle
are exclusive (serialized).