Skip to content

Commit

Permalink
Document the new RoTxn and RwTxn commit methods
Browse files Browse the repository at this point in the history
  • Loading branch information
Kerollmops committed Apr 8, 2023
1 parent 270d08c commit 255e88c
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 17 deletions.
8 changes: 5 additions & 3 deletions heed/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@ use heed_traits as traits;
pub use {bytemuck, byteorder, heed_types as types};

use self::cursor::{RoCursor, RwCursor};
pub use self::db::{Database, PolyDatabase};
pub use self::db::{
Database, OpenedDatabases, PolyDatabase, RememberedDatabase, RememberedPolyDatabase,
};
pub use self::env::{env_closing_event, CompactionOption, Env, EnvClosingEvent, EnvOpenOptions};
pub use self::iter::{
RoIter, RoPrefix, RoRange, RoRevIter, RoRevPrefix, RoRevRange, RwIter, RwPrefix, RwRange,
Expand Down Expand Up @@ -147,7 +149,7 @@ mod tests {
}

macro_rules! assert_eq_env_db_txn {
($database:ident, $txn:ident) => {
($database:expr, $txn:expr) => {
assert!(
$database.env_ident == $txn.env_mut_ptr() as usize,
"The database environment doesn't match the transaction's environment"
Expand All @@ -156,7 +158,7 @@ macro_rules! assert_eq_env_db_txn {
}

macro_rules! assert_eq_env_txn {
($env:ident, $txn:ident) => {
($env:expr, $txn:expr) => {
assert!(
$env.env_mut_ptr() == $txn.env_mut_ptr(),
"The environment doesn't match the transaction's environment"
Expand Down
136 changes: 122 additions & 14 deletions heed/src/txn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ use std::ptr;

use crate::mdb::error::mdb_result;
use crate::mdb::ffi;
use crate::{Env, Result};
use crate::{
assert_eq_env_db_txn, Database, Env, OpenedDatabases, PolyDatabase, RememberedDatabase,
RememberedPolyDatabase, Result,
};

/// A read-only transaction.
pub struct RoTxn<'e> {
Expand All @@ -30,6 +33,108 @@ impl<'e> RoTxn<'e> {
pub(crate) fn env_mut_ptr(&self) -> *mut ffi::MDB_env {
self.env.env_mut_ptr()
}

/// Abort and loose the temporary database handles you opened in this transaction.
pub fn abort(mut self) {
abort_txn(self.txn);
self.txn = ptr::null_mut();
}

/// Keep the database you opened in this transaction by committing them.
///
/// # Example: Opening and commit a Database
///
/// You can open a database with a read transaction, use it in the transaction itself
/// but you'll need to [`RoTxn::remember_database`] and [`OpenedDatabases::retrieve_database`]
/// once you committed your transaction to be able to retrieve a global handle that
/// you can store anywhere.
///
/// ```
/// # use std::fs;
/// # use std::path::Path;
/// # use heed::EnvOpenOptions;
/// use heed::Database;
/// use heed::byteorder::BigEndian;
/// use heed::types::*;
///
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// # let dir = tempfile::tempdir()?;
/// # let env = EnvOpenOptions::new()
/// # .map_size(10 * 1024 * 1024) // 10MB
/// # .open(dir.path())?;
/// type BEU32 = U32<BigEndian>;
///
/// let rtxn = env.read_txn()?;
/// let photos: Database<BEU32, Str> = env.open_database(&rtxn, None)?.unwrap();
/// let remembered_photos = rtxn.remember_database(photos);
/// let opened_databases = rtxn.commit()?;
///
/// // You can retrieve a global handle to your database now that it has been committed.
/// let photos = opened_databases.retrieve_database(remembered_photos);
/// let rtxn = env.read_txn()?;
/// assert_eq!(photos.len(&rtxn)?, 0);
/// # Ok(()) }
/// ```
///
/// # How not to use `Database`s
///
/// You must make sure to commit your transaction to be able to retrieve global [`Database`] handles.
/// The returned temporary handles can only be used in the scope of the transaction, not longer.
///
/// ```compile_fail
/// # use std::fs;
/// # use std::path::Path;
/// # use heed::EnvOpenOptions;
/// use heed::Database;
/// use heed::byteorder::BigEndian;
/// use heed::types::*;
///
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// # let dir = tempfile::tempdir()?;
/// # let env = EnvOpenOptions::new()
/// # .map_size(10 * 1024 * 1024) // 10MB
/// # .open(dir.path())?;
/// type BEU32 = U32<BigEndian>;
///
/// let rtxn = env.read_txn()?;
/// let photos: Database<BEU32, Str> = env.open_database(&rtxn, None)?.unwrap();
/// rtxn.abort();
///
/// // You can't use photos has you forget to remember it and commit.
/// let rtxn = env.read_txn()?;
/// assert_eq!(photos.len(&rtxn)?, 0);
/// # Ok(()) }
/// ```
pub fn commit(mut self) -> Result<OpenedDatabases> {
// TODO make sure we have a unique identifier between the `OpenedDatabases`
// and the `RememberedPolyDatabase` types to make sure that we are not trying
// to globally keep an inalid database.
let result = unsafe { mdb_result(ffi::mdb_txn_commit(self.txn)) };
self.txn = ptr::null_mut();
result.map_err(Into::into).map(|()| OpenedDatabases { unique_txn_id: 0 })
}

/// Remember a `PolyDatabase` to be able to retrieve it after a [`RoTxn::commit`]/[`RwTxn::commit`].
pub fn remember_poly_database<'t>(
&'t self,
poly_database: PolyDatabase<'t>,
) -> RememberedPolyDatabase {
// TODO test that the database comes from the same txn
assert_eq_env_db_txn!(poly_database, self);
let PolyDatabase { env_ident, dbi, .. } = poly_database;
RememberedPolyDatabase { unique_txn_id: 0, env_ident, dbi }
}

/// Remember a `Database` to be able to retrieve it after a [`RoTxn::commit`]/[`RwTxn::commit`].
pub fn remember_database<'t, KC, DC>(
&'t self,
database: Database<'t, KC, DC>,
) -> RememberedDatabase<KC, DC> {
// TODO test that the database comes from the same txn
assert_eq_env_db_txn!(database.dyndb, self);
let Database { dyndb: PolyDatabase { env_ident, dbi, .. }, marker } = database;
RememberedDatabase { unique_txn_id: 0, env_ident, dbi, marker }
}
}

impl Drop for RoTxn<'_> {
Expand All @@ -43,12 +148,6 @@ impl Drop for RoTxn<'_> {
#[cfg(feature = "sync-read-txn")]
unsafe impl<T> Sync for RoTxn<'_> {}

fn abort_txn(txn: *mut ffi::MDB_txn) {
// Asserts that the transaction hasn't been already committed.
assert!(!txn.is_null());
unsafe { ffi::mdb_txn_abort(txn) }
}

/// A read-write transaction.
pub struct RwTxn<'p> {
pub(crate) txn: RoTxn<'p>,
Expand Down Expand Up @@ -76,15 +175,18 @@ impl<'p> RwTxn<'p> {
self.txn.env.env_mut_ptr()
}

pub fn commit(mut self) -> Result<()> {
let result = unsafe { mdb_result(ffi::mdb_txn_commit(self.txn.txn)) };
self.txn.txn = ptr::null_mut();
result.map_err(Into::into)
/// Keep the changes you made to the environement and database you opened in this transaction
/// by committing them.
///
/// Read the documentation of the [`RoTxn::commit`] method to learn more
/// on how to retrieve global database handles.
pub fn commit(self) -> Result<OpenedDatabases> {
self.txn.commit()
}

pub fn abort(mut self) {
abort_txn(self.txn.txn);
self.txn.txn = ptr::null_mut();
/// Abort and loose the temporary database handles you opened in this transaction.
pub fn abort(self) {
self.txn.abort();
}
}

Expand All @@ -95,3 +197,9 @@ impl<'p> Deref for RwTxn<'p> {
&self.txn
}
}

fn abort_txn(txn: *mut ffi::MDB_txn) {
// Asserts that the transaction hasn't already been committed.
assert!(!txn.is_null());
unsafe { ffi::mdb_txn_abort(txn) }
}

0 comments on commit 255e88c

Please sign in to comment.