Skip to content

Commit 03c06a6

Browse files
committed
Document the new RoTxn and RwTxn commit methods
1 parent d76b201 commit 03c06a6

File tree

2 files changed

+127
-17
lines changed

2 files changed

+127
-17
lines changed

heed/src/lib.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,9 @@ use heed_traits as traits;
6262
pub use {bytemuck, byteorder, heed_types as types};
6363

6464
use self::cursor::{RoCursor, RwCursor};
65-
pub use self::db::{Database, PolyDatabase};
65+
pub use self::db::{
66+
Database, OpenedDatabases, PolyDatabase, RememberedDatabase, RememberedPolyDatabase,
67+
};
6668
pub use self::env::{env_closing_event, CompactionOption, Env, EnvClosingEvent, EnvOpenOptions};
6769
pub use self::iter::{
6870
RoIter, RoPrefix, RoRange, RoRevIter, RoRevPrefix, RoRevRange, RwIter, RwPrefix, RwRange,
@@ -147,7 +149,7 @@ mod tests {
147149
}
148150

149151
macro_rules! assert_eq_env_db_txn {
150-
($database:ident, $txn:ident) => {
152+
($database:expr, $txn:expr) => {
151153
assert!(
152154
$database.env_ident == $txn.env_mut_ptr() as usize,
153155
"The database environment doesn't match the transaction's environment"
@@ -156,7 +158,7 @@ macro_rules! assert_eq_env_db_txn {
156158
}
157159

158160
macro_rules! assert_eq_env_txn {
159-
($env:ident, $txn:ident) => {
161+
($env:expr, $txn:expr) => {
160162
assert!(
161163
$env.env_mut_ptr() == $txn.env_mut_ptr(),
162164
"The environment doesn't match the transaction's environment"

heed/src/txn.rs

Lines changed: 122 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ use std::ptr;
33

44
use crate::mdb::error::mdb_result;
55
use crate::mdb::ffi;
6-
use crate::{Env, Result};
6+
use crate::{
7+
assert_eq_env_db_txn, Database, Env, OpenedDatabases, PolyDatabase, RememberedDatabase,
8+
RememberedPolyDatabase, Result,
9+
};
710

811
/// A read-only transaction.
912
pub struct RoTxn<'e> {
@@ -30,6 +33,108 @@ impl<'e> RoTxn<'e> {
3033
pub(crate) fn env_mut_ptr(&self) -> *mut ffi::MDB_env {
3134
self.env.env_mut_ptr()
3235
}
36+
37+
/// Abort and loose the temporary database handles you opened in this transaction.
38+
pub fn abort(mut self) {
39+
abort_txn(self.txn);
40+
self.txn = ptr::null_mut();
41+
}
42+
43+
/// Keep the database you opened in this transaction by committing them.
44+
///
45+
/// # Example: Opening and commit a Database
46+
///
47+
/// You can open a database with a read transaction, use it in the transaction itself
48+
/// but you'll need to [`RoTxn::remember_database`] and [`OpenedDatabases::retrieve_database`]
49+
/// once you committed your transaction to be able to retrieve a global handle that
50+
/// you can store anywhere.
51+
///
52+
/// ```
53+
/// # use std::fs;
54+
/// # use std::path::Path;
55+
/// # use heed::EnvOpenOptions;
56+
/// use heed::Database;
57+
/// use heed::byteorder::BigEndian;
58+
/// use heed::types::*;
59+
///
60+
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
61+
/// # let dir = tempfile::tempdir()?;
62+
/// # let env = EnvOpenOptions::new()
63+
/// # .map_size(10 * 1024 * 1024) // 10MB
64+
/// # .open(dir.path())?;
65+
/// type BEU32 = U32<BigEndian>;
66+
///
67+
/// let rtxn = env.read_txn()?;
68+
/// let photos: Database<BEU32, Str> = env.open_database(&rtxn, None)?.unwrap();
69+
/// let remembered_photos = rtxn.remember_database(photos);
70+
/// let opened_databases = rtxn.commit()?;
71+
///
72+
/// // You can retrieve a global handle to your database now that it has been committed.
73+
/// let photos = opened_databases.retrieve_database(remembered_photos);
74+
/// let rtxn = env.read_txn()?;
75+
/// assert_eq!(photos.len(&rtxn)?, 0);
76+
/// # Ok(()) }
77+
/// ```
78+
///
79+
/// # How not to use `Database`s
80+
///
81+
/// You must make sure to commit your transaction to be able to retrieve global [`Database`] handles.
82+
/// The returned temporary handles can only be used in the scope of the transaction, not longer.
83+
///
84+
/// ```compile_fail
85+
/// # use std::fs;
86+
/// # use std::path::Path;
87+
/// # use heed::EnvOpenOptions;
88+
/// use heed::Database;
89+
/// use heed::byteorder::BigEndian;
90+
/// use heed::types::*;
91+
///
92+
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
93+
/// # let dir = tempfile::tempdir()?;
94+
/// # let env = EnvOpenOptions::new()
95+
/// # .map_size(10 * 1024 * 1024) // 10MB
96+
/// # .open(dir.path())?;
97+
/// type BEU32 = U32<BigEndian>;
98+
///
99+
/// let rtxn = env.read_txn()?;
100+
/// let photos: Database<BEU32, Str> = env.open_database(&rtxn, None)?.unwrap();
101+
/// rtxn.abort();
102+
///
103+
/// // You can't use photos has you forget to remember it and commit.
104+
/// let rtxn = env.read_txn()?;
105+
/// assert_eq!(photos.len(&rtxn)?, 0);
106+
/// # Ok(()) }
107+
/// ```
108+
pub fn commit(mut self) -> Result<OpenedDatabases> {
109+
// TODO make sure we have a unique identifier between the `OpenedDatabases`
110+
// and the `RememberedPolyDatabase` types to make sure that we are not trying
111+
// to globally keep an inalid database.
112+
let result = unsafe { mdb_result(ffi::mdb_txn_commit(self.txn)) };
113+
self.txn = ptr::null_mut();
114+
result.map_err(Into::into).map(|()| OpenedDatabases { unique_txn_id: 0 })
115+
}
116+
117+
/// Remember a `PolyDatabase` to be able to retrieve it after a [`RoTxn::commit`]/[`RwTxn::commit`].
118+
pub fn remember_poly_database<'t>(
119+
&'t self,
120+
poly_database: PolyDatabase<'t>,
121+
) -> RememberedPolyDatabase {
122+
// TODO test that the database comes from the same txn
123+
assert_eq_env_db_txn!(poly_database, self);
124+
let PolyDatabase { env_ident, dbi, .. } = poly_database;
125+
RememberedPolyDatabase { unique_txn_id: 0, env_ident, dbi }
126+
}
127+
128+
/// Remember a `Database` to be able to retrieve it after a [`RoTxn::commit`]/[`RwTxn::commit`].
129+
pub fn remember_database<'t, KC, DC>(
130+
&'t self,
131+
database: Database<'t, KC, DC>,
132+
) -> RememberedDatabase<KC, DC> {
133+
// TODO test that the database comes from the same txn
134+
assert_eq_env_db_txn!(database.dyndb, self);
135+
let Database { dyndb: PolyDatabase { env_ident, dbi, .. }, marker } = database;
136+
RememberedDatabase { unique_txn_id: 0, env_ident, dbi, marker }
137+
}
33138
}
34139

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

46-
fn abort_txn(txn: *mut ffi::MDB_txn) {
47-
// Asserts that the transaction hasn't been already committed.
48-
assert!(!txn.is_null());
49-
unsafe { ffi::mdb_txn_abort(txn) }
50-
}
51-
52151
/// A read-write transaction.
53152
pub struct RwTxn<'p> {
54153
pub(crate) txn: RoTxn<'p>,
@@ -76,15 +175,18 @@ impl<'p> RwTxn<'p> {
76175
self.txn.env.env_mut_ptr()
77176
}
78177

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

85-
pub fn abort(mut self) {
86-
abort_txn(self.txn.txn);
87-
self.txn.txn = ptr::null_mut();
187+
/// Abort and loose the temporary database handles you opened in this transaction.
188+
pub fn abort(self) {
189+
self.txn.abort();
88190
}
89191
}
90192

@@ -95,3 +197,9 @@ impl<'p> Deref for RwTxn<'p> {
95197
&self.txn
96198
}
97199
}
200+
201+
fn abort_txn(txn: *mut ffi::MDB_txn) {
202+
// Asserts that the transaction hasn't already been committed.
203+
assert!(!txn.is_null());
204+
unsafe { ffi::mdb_txn_abort(txn) }
205+
}

0 commit comments

Comments
 (0)