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

Additional to implementing Disk, the following helper functions are also available.

source

pub fn is_initialized(&self) -> bool

This function tests whether a database handle has been initialized.

source

pub fn is_connected(&self) -> bool

This function tests whether a database handle is connected.

source

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.

source

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

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§

fn default() -> Self

Returns the “default value” for a type. Read more
source§

impl Disk for LsmDb

Custom implementation of Disk for LsmDb.

source§

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>

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>

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:

  1. The resources that belong to the memory handle will be leaked.
  2. 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>

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>

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>

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>

This function deletes the open interval of keys (being, end) (in a transactional manner as well).

source§

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>

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>

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>

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>

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

Drop for LsmDb so that it gets properly terminated when it goes out of scope for example.

source§

fn drop(&mut self)

Executes the destructor for this type. Read more
source§

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.

source§

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).

Auto Trait Implementations§

Blanket Implementations§

source§

impl<T> Any for T
where T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T> Instrument for T

source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
source§

impl<T> WithSubscriber for T

source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more