rename transaction and cursor opening functions; improve docs

without.crypto
Dan Burkert 10 years ago
parent 25f240282e
commit 9facaae124
  1. 50
      src/cursor.rs
  2. 66
      src/database.rs
  3. 42
      src/environment.rs
  4. 2
      src/lib.rs
  5. 56
      src/transaction.rs

@ -20,8 +20,8 @@ pub trait Cursor<'txn> {
/// Cursor extension methods. /// Cursor extension methods.
pub trait CursorExt<'txn> : Cursor<'txn> { pub trait CursorExt<'txn> : Cursor<'txn> {
/// Retrieves a key/data pair from the cursor. Depending on the cursor op, the current key is /// Retrieves a key/data pair from the cursor. Depending on the cursor op, the current key may
/// returned. /// be returned.
fn get(&self, fn get(&self,
key: Option<&[u8]>, key: Option<&[u8]>,
data: Option<&[u8]>, data: Option<&[u8]>,
@ -49,7 +49,7 @@ pub trait CursorExt<'txn> : Cursor<'txn> {
impl<'txn, T> CursorExt<'txn> for T where T: Cursor<'txn> {} impl<'txn, T> CursorExt<'txn> for T where T: Cursor<'txn> {}
/// A read-only cursor for navigating items within a database. /// A read-only cursor for navigating the items within a database.
pub struct RoCursor<'txn> { pub struct RoCursor<'txn> {
cursor: *mut ffi::MDB_cursor, cursor: *mut ffi::MDB_cursor,
_no_sync: marker::NoSync, _no_sync: marker::NoSync,
@ -73,7 +73,7 @@ impl <'txn> Drop for RoCursor<'txn> {
impl <'txn> RoCursor<'txn> { impl <'txn> RoCursor<'txn> {
/// Creates a new read-only cursor in the given database and transaction. Prefer using /// Creates a new read-only cursor in the given database and transaction. Prefer using
/// `Transaction::open_cursor()`. /// `Transaction::open_cursor`.
#[doc(hidden)] #[doc(hidden)]
pub fn new(txn: &'txn Transaction, db: Database) -> LmdbResult<RoCursor<'txn>> { pub fn new(txn: &'txn Transaction, db: Database) -> LmdbResult<RoCursor<'txn>> {
let mut cursor: *mut ffi::MDB_cursor = ptr::null_mut(); let mut cursor: *mut ffi::MDB_cursor = ptr::null_mut();
@ -111,7 +111,7 @@ impl <'txn> Drop for RwCursor<'txn> {
impl <'txn> RwCursor<'txn> { impl <'txn> RwCursor<'txn> {
/// Creates a new read-only cursor in the given database and transaction. Prefer using /// Creates a new read-only cursor in the given database and transaction. Prefer using
/// `WriteTransaction::open_write_cursor()`. /// `RwTransaction::open_rw_cursor`.
#[doc(hidden)] #[doc(hidden)]
pub fn new(txn: &'txn Transaction, db: Database) -> LmdbResult<RwCursor<'txn>> { pub fn new(txn: &'txn Transaction, db: Database) -> LmdbResult<RwCursor<'txn>> {
let mut cursor: *mut ffi::MDB_cursor = ptr::null_mut(); let mut cursor: *mut ffi::MDB_cursor = ptr::null_mut();
@ -126,7 +126,7 @@ impl <'txn> RwCursor<'txn> {
/// Puts a key/data pair into the database. The cursor will be positioned at the new data item, /// Puts a key/data pair into the database. The cursor will be positioned at the new data item,
/// or on failure usually near it. /// or on failure usually near it.
pub fn put(&self, pub fn put(&mut self,
key: &[u8], key: &[u8],
data: &[u8], data: &[u8],
flags: WriteFlags) flags: WriteFlags)
@ -149,10 +149,8 @@ impl <'txn> RwCursor<'txn> {
/// ///
/// `WriteFlags::NO_DUP_DATA` may be used to delete all data items for the current key, if the /// `WriteFlags::NO_DUP_DATA` may be used to delete all data items for the current key, if the
/// database was opened with `DatabaseFlags::DUP_SORT`. /// database was opened with `DatabaseFlags::DUP_SORT`.
pub fn del(&self, flags: WriteFlags) -> LmdbResult<()> { pub fn del(&mut self, flags: WriteFlags) -> LmdbResult<()> {
unsafe { unsafe { lmdb_result(ffi::mdb_cursor_del(self.cursor(), flags.bits())) }
lmdb_result(ffi::mdb_cursor_del(self.cursor(), flags.bits()))
}
} }
} }
@ -237,15 +235,15 @@ mod test {
(b"key3", b"val3")); (b"key3", b"val3"));
{ {
let mut txn = env.begin_write_txn().unwrap(); let mut txn = env.begin_rw_txn().unwrap();
for &(key, data) in items.iter() { for &(key, data) in items.iter() {
txn.put(db, key, data, WriteFlags::empty()).unwrap(); txn.put(db, key, data, WriteFlags::empty()).unwrap();
} }
txn.commit().unwrap(); txn.commit().unwrap();
} }
let txn = env.begin_read_txn().unwrap(); let txn = env.begin_ro_txn().unwrap();
let mut cursor = txn.open_read_cursor(db).unwrap(); let mut cursor = txn.open_ro_cursor(db).unwrap();
assert_eq!(items, cursor.iter().collect::<Vec<(&[u8], &[u8])>>()); assert_eq!(items, cursor.iter().collect::<Vec<(&[u8], &[u8])>>());
} }
@ -255,12 +253,12 @@ mod test {
let env = Environment::new().open(dir.path(), io::USER_RWX).unwrap(); let env = Environment::new().open(dir.path(), io::USER_RWX).unwrap();
let db = env.open_db(None).unwrap(); let db = env.open_db(None).unwrap();
let mut txn = env.begin_write_txn().unwrap(); let mut txn = env.begin_rw_txn().unwrap();
txn.put(db, b"key1", b"val1", WriteFlags::empty()).unwrap(); txn.put(db, b"key1", b"val1", WriteFlags::empty()).unwrap();
txn.put(db, b"key2", b"val2", WriteFlags::empty()).unwrap(); txn.put(db, b"key2", b"val2", WriteFlags::empty()).unwrap();
txn.put(db, b"key3", b"val3", WriteFlags::empty()).unwrap(); txn.put(db, b"key3", b"val3", WriteFlags::empty()).unwrap();
let cursor = txn.open_read_cursor(db).unwrap(); let cursor = txn.open_ro_cursor(db).unwrap();
assert_eq!((Some(b"key1"), b"val1"), assert_eq!((Some(b"key1"), b"val1"),
cursor.get(None, None, MDB_FIRST).unwrap()); cursor.get(None, None, MDB_FIRST).unwrap());
assert_eq!((Some(b"key1"), b"val1"), assert_eq!((Some(b"key1"), b"val1"),
@ -285,7 +283,7 @@ mod test {
let env = Environment::new().open(dir.path(), io::USER_RWX).unwrap(); let env = Environment::new().open(dir.path(), io::USER_RWX).unwrap();
let db = env.create_db(None, DUP_SORT).unwrap(); let db = env.create_db(None, DUP_SORT).unwrap();
let mut txn = env.begin_write_txn().unwrap(); let mut txn = env.begin_rw_txn().unwrap();
txn.put(db, b"key1", b"val1", WriteFlags::empty()).unwrap(); txn.put(db, b"key1", b"val1", WriteFlags::empty()).unwrap();
txn.put(db, b"key1", b"val2", WriteFlags::empty()).unwrap(); txn.put(db, b"key1", b"val2", WriteFlags::empty()).unwrap();
txn.put(db, b"key1", b"val3", WriteFlags::empty()).unwrap(); txn.put(db, b"key1", b"val3", WriteFlags::empty()).unwrap();
@ -293,7 +291,7 @@ mod test {
txn.put(db, b"key2", b"val2", WriteFlags::empty()).unwrap(); txn.put(db, b"key2", b"val2", WriteFlags::empty()).unwrap();
txn.put(db, b"key2", b"val3", WriteFlags::empty()).unwrap(); txn.put(db, b"key2", b"val3", WriteFlags::empty()).unwrap();
let cursor = txn.open_read_cursor(db).unwrap(); let cursor = txn.open_ro_cursor(db).unwrap();
assert_eq!((Some(b"key1"), b"val1"), assert_eq!((Some(b"key1"), b"val1"),
cursor.get(None, None, MDB_FIRST).unwrap()); cursor.get(None, None, MDB_FIRST).unwrap());
assert_eq!((None, b"val1"), assert_eq!((None, b"val1"),
@ -331,7 +329,7 @@ mod test {
let env = Environment::new().open(dir.path(), io::USER_RWX).unwrap(); let env = Environment::new().open(dir.path(), io::USER_RWX).unwrap();
let db = env.create_db(None, DUP_SORT | DUP_FIXED).unwrap(); let db = env.create_db(None, DUP_SORT | DUP_FIXED).unwrap();
let mut txn = env.begin_write_txn().unwrap(); let mut txn = env.begin_rw_txn().unwrap();
txn.put(db, b"key1", b"val1", WriteFlags::empty()).unwrap(); txn.put(db, b"key1", b"val1", WriteFlags::empty()).unwrap();
txn.put(db, b"key1", b"val2", WriteFlags::empty()).unwrap(); txn.put(db, b"key1", b"val2", WriteFlags::empty()).unwrap();
txn.put(db, b"key1", b"val3", WriteFlags::empty()).unwrap(); txn.put(db, b"key1", b"val3", WriteFlags::empty()).unwrap();
@ -339,7 +337,7 @@ mod test {
txn.put(db, b"key2", b"val5", WriteFlags::empty()).unwrap(); txn.put(db, b"key2", b"val5", WriteFlags::empty()).unwrap();
txn.put(db, b"key2", b"val6", WriteFlags::empty()).unwrap(); txn.put(db, b"key2", b"val6", WriteFlags::empty()).unwrap();
let cursor = txn.open_read_cursor(db).unwrap(); let cursor = txn.open_ro_cursor(db).unwrap();
assert_eq!((Some(b"key1"), b"val1"), assert_eq!((Some(b"key1"), b"val1"),
cursor.get(None, None, MDB_FIRST).unwrap()); cursor.get(None, None, MDB_FIRST).unwrap());
assert_eq!((None, b"val1val2val3"), assert_eq!((None, b"val1val2val3"),
@ -353,8 +351,8 @@ mod test {
let env = Environment::new().open(dir.path(), io::USER_RWX).unwrap(); let env = Environment::new().open(dir.path(), io::USER_RWX).unwrap();
let db = env.open_db(None).unwrap(); let db = env.open_db(None).unwrap();
let mut txn = env.begin_write_txn().unwrap(); let mut txn = env.begin_rw_txn().unwrap();
let cursor = txn.open_write_cursor(db).unwrap(); let mut cursor = txn.open_rw_cursor(db).unwrap();
cursor.put(b"key1", b"val1", WriteFlags::empty()).unwrap(); cursor.put(b"key1", b"val1", WriteFlags::empty()).unwrap();
cursor.put(b"key2", b"val2", WriteFlags::empty()).unwrap(); cursor.put(b"key2", b"val2", WriteFlags::empty()).unwrap();
@ -374,10 +372,10 @@ mod test {
let n = 100; let n = 100;
let (_dir, env) = setup_bench_db(n); let (_dir, env) = setup_bench_db(n);
let db = env.open_db(None).unwrap(); let db = env.open_db(None).unwrap();
let txn = env.begin_read_txn().unwrap(); let txn = env.begin_ro_txn().unwrap();
b.iter(|| { b.iter(|| {
let mut cursor = txn.open_read_cursor(db).unwrap(); let mut cursor = txn.open_ro_cursor(db).unwrap();
let mut i = 0; let mut i = 0;
let mut count = 0u32; let mut count = 0u32;
@ -397,10 +395,10 @@ mod test {
let n = 100; let n = 100;
let (_dir, env) = setup_bench_db(n); let (_dir, env) = setup_bench_db(n);
let db = env.open_db(None).unwrap(); let db = env.open_db(None).unwrap();
let txn = env.begin_read_txn().unwrap(); let txn = env.begin_ro_txn().unwrap();
b.iter(|| { b.iter(|| {
let cursor = txn.open_read_cursor(db).unwrap(); let cursor = txn.open_ro_cursor(db).unwrap();
let mut i = 0; let mut i = 0;
let mut count = 0u32; let mut count = 0u32;
@ -422,7 +420,7 @@ mod test {
let db = env.open_db(None).unwrap(); let db = env.open_db(None).unwrap();
let dbi: MDB_dbi = db.dbi(); let dbi: MDB_dbi = db.dbi();
let _txn = env.begin_read_txn().unwrap(); let _txn = env.begin_ro_txn().unwrap();
let txn = _txn.txn(); let txn = _txn.txn();
let mut key = MDB_val { mv_size: 0, mv_data: ptr::null_mut() }; let mut key = MDB_val { mv_size: 0, mv_data: ptr::null_mut() };

@ -1,53 +1,73 @@
use std::kinds::marker;
use std::ptr; use std::ptr;
use ffi::*; use ffi;
use error::{LmdbResult, lmdb_result}; use error::{LmdbResult, lmdb_result};
use flags::DatabaseFlags; use flags::DatabaseFlags;
use transaction::{RoTransaction, RwTransaction, Transaction}; use transaction::{RwTransaction, Transaction};
/// A handle to an individual database in an environment. /// A handle to an individual database in an environment.
/// ///
/// A database handle denotes the name and parameters of a database in an environment. The database /// A database handle denotes the name and parameters of a database in an environment.
/// may not exist in the environment (for instance, if the database is opened during a transaction
/// that has not yet committed).
#[deriving(Clone, Copy)] #[deriving(Clone, Copy)]
pub struct Database<'env> { pub struct Database {
dbi: MDB_dbi, dbi: ffi::MDB_dbi,
_marker: marker::ContravariantLifetime<'env>,
} }
impl <'env> Database<'env> { impl Database {
pub unsafe fn open<'e>(txn: &RoTransaction<'e>, /// Opens a database in the provided transaction.
///
/// If `name` is `None`, then the default database will be opened, otherwise a named database
/// will be opened. The database handle will be private to the transaction until the transaction
/// is successfully committed. If the transaction is aborted the returned database handle
/// should no longer be used.
///
/// ## Unsafety
///
/// * This function (as well as `Environment::open_db`, `Environment::create_db`, and
/// `Database::create`) **must not** be called from multiple concurrent transactions in the same
/// environment. A transaction which uses this function must finish (either commit or abort)
/// before any other transaction may use this function.
pub unsafe fn open(txn: &Transaction,
name: Option<&str>) name: Option<&str>)
-> LmdbResult<Database<'e>> { -> LmdbResult<Database> {
let c_name = name.map(|n| n.to_c_str()); let c_name = name.map(|n| n.to_c_str());
let name_ptr = if let Some(ref c_name) = c_name { c_name.as_ptr() } else { ptr::null() }; let name_ptr = if let Some(ref c_name) = c_name { c_name.as_ptr() } else { ptr::null() };
let mut dbi: MDB_dbi = 0; let mut dbi: ffi::MDB_dbi = 0;
try!(lmdb_result(mdb_dbi_open(txn.txn(), name_ptr, 0, &mut dbi))); try!(lmdb_result(ffi::mdb_dbi_open(txn.txn(), name_ptr, 0, &mut dbi)));
Ok(Database { dbi: dbi, Ok(Database { dbi: dbi })
_marker: marker::ContravariantLifetime::<'e> })
} }
pub unsafe fn create<'e>(txn: &RwTransaction<'e>, /// Opens a handle in the provided transaction, creating the database if necessary.
///
/// If `name` is `None`, then the default database will be opened, otherwise a named database
/// will be opened. The database handle will be private to the transaction until the transaction
/// is successfully committed. If the transaction is aborted the returned database handle
/// should no longer be used.
///
/// ## Unsafety
///
/// * This function (as well as `Environment::open_db`, `Environment::create_db`, and
/// `Database::open`) **must not** be called from multiple concurrent transactions in the same
/// environment. A transaction which uses this function must finish (either commit or abort)
/// before any other transaction may use this function.
pub unsafe fn create(txn: &RwTransaction,
name: Option<&str>, name: Option<&str>,
flags: DatabaseFlags) flags: DatabaseFlags)
-> LmdbResult<Database<'e>> { -> LmdbResult<Database> {
let c_name = name.map(|n| n.to_c_str()); let c_name = name.map(|n| n.to_c_str());
let name_ptr = if let Some(ref c_name) = c_name { c_name.as_ptr() } else { ptr::null() }; let name_ptr = if let Some(ref c_name) = c_name { c_name.as_ptr() } else { ptr::null() };
let mut dbi: MDB_dbi = 0; let mut dbi: ffi::MDB_dbi = 0;
try!(lmdb_result(mdb_dbi_open(txn.txn(), name_ptr, flags.bits() | MDB_CREATE, &mut dbi))); try!(lmdb_result(ffi::mdb_dbi_open(txn.txn(), name_ptr, flags.bits() | ffi::MDB_CREATE, &mut dbi)));
Ok(Database { dbi: dbi, Ok(Database { dbi: dbi })
_marker: marker::ContravariantLifetime::<'e> })
} }
/// Returns the underlying LMDB database handle. /// Returns the underlying LMDB database handle.
/// ///
/// The caller **must** ensure that the handle is not used after the lifetime of the /// The caller **must** ensure that the handle is not used after the lifetime of the
/// environment, or after the database handle has been closed. /// environment, or after the database handle has been closed.
pub fn dbi(&self) -> MDB_dbi { pub fn dbi(&self) -> ffi::MDB_dbi {
self.dbi self.dbi
} }
} }

@ -46,17 +46,20 @@ impl Environment {
/// case the environment must be configured to allow named databases through /// case the environment must be configured to allow named databases through
/// `EnvironmentBuilder::set_max_dbs`. /// `EnvironmentBuilder::set_max_dbs`.
/// ///
/// The returned database handle may be shared among transactions. /// The returned database handle may be shared among any transaction in the environment.
pub fn open_db<'env>(&'env self, name: Option<&str>) -> LmdbResult<Database<'env>> { ///
/// This function will fail with `LmdbError::BadRslot` if called by a thread which has an ongoing
/// transaction.
pub fn open_db<'env>(&'env self, name: Option<&str>) -> LmdbResult<Database> {
let mutex = self.dbi_open_mutex.lock(); let mutex = self.dbi_open_mutex.lock();
let txn = try!(self.begin_read_txn()); let txn = try!(self.begin_ro_txn());
let db = unsafe { try!(Database::open(&txn, name)) }; let db = unsafe { try!(Database::open(&txn, name)) };
try!(txn.commit()); try!(txn.commit());
drop(mutex); drop(mutex);
Ok(db) Ok(db)
} }
/// Opens a handle to an LMDB database, opening the database if necessary. /// Opens a handle to an LMDB database, creating the database if necessary.
/// ///
/// If the database is already created, the given option flags will be added to it. /// If the database is already created, the given option flags will be added to it.
/// ///
@ -66,21 +69,24 @@ impl Environment {
/// case the environment must be configured to allow named databases through /// case the environment must be configured to allow named databases through
/// `EnvironmentBuilder::set_max_dbs`. /// `EnvironmentBuilder::set_max_dbs`.
/// ///
/// The returned database handle may be shared among transactions. /// The returned database handle may be shared among any transaction in the environment.
///
/// This function will fail with `LmdbError::BadRslot` if called by a thread with an open
/// transaction.
pub fn create_db<'env>(&'env self, pub fn create_db<'env>(&'env self,
name: Option<&str>, name: Option<&str>,
flags: DatabaseFlags) flags: DatabaseFlags)
-> LmdbResult<Database<'env>> { -> LmdbResult<Database> {
let mutex = self.dbi_open_mutex.lock(); let mutex = self.dbi_open_mutex.lock();
let txn = try!(self.begin_write_txn()); let txn = try!(self.begin_rw_txn());
let db = unsafe { try!(Database::create(&txn, name, flags)) }; let db = unsafe { try!(Database::create(&txn, name, flags)) };
try!(txn.commit()); try!(txn.commit());
drop(mutex); drop(mutex);
Ok(db) Ok(db)
} }
pub fn get_db_flags<'env>(&'env self, db: Database<'env>) -> LmdbResult<DatabaseFlags> { pub fn get_db_flags<'env>(&'env self, db: Database) -> LmdbResult<DatabaseFlags> {
let txn = try!(self.begin_read_txn()); let txn = try!(self.begin_ro_txn());
let mut flags: c_uint = 0; let mut flags: c_uint = 0;
unsafe { unsafe {
try!(lmdb_result(ffi::mdb_dbi_flags(txn.txn(), db.dbi(), &mut flags))); try!(lmdb_result(ffi::mdb_dbi_flags(txn.txn(), db.dbi(), &mut flags)));
@ -89,19 +95,19 @@ impl Environment {
} }
/// Create a read-only transaction for use with the environment. /// Create a read-only transaction for use with the environment.
pub fn begin_read_txn<'env>(&'env self) -> LmdbResult<RoTransaction<'env>> { pub fn begin_ro_txn<'env>(&'env self) -> LmdbResult<RoTransaction<'env>> {
RoTransaction::new(self) RoTransaction::new(self)
} }
/// Create a read-write transaction for use with the environment. This method will block while /// Create a read-write transaction for use with the environment. This method will block while
/// there are any other read-write transactions open on the environment. /// there are any other read-write transactions open on the environment.
pub fn begin_write_txn<'env>(&'env self) -> LmdbResult<RwTransaction<'env>> { pub fn begin_rw_txn<'env>(&'env self) -> LmdbResult<RwTransaction<'env>> {
RwTransaction::new(self) RwTransaction::new(self)
} }
/// Flush data buffers to disk. /// Flush data buffers to disk.
/// ///
/// Data is always written to disk when `Transaction::commit()` is called, but the operating /// Data is always written to disk when `Transaction::commit` is called, but the operating
/// system may keep it buffered. LMDB always flushes the OS buffers upon commit as well, unless /// system may keep it buffered. LMDB always flushes the OS buffers upon commit as well, unless
/// the environment was opened with `MDB_NOSYNC` or in part `MDB_NOMETASYNC`. /// the environment was opened with `MDB_NOSYNC` or in part `MDB_NOMETASYNC`.
pub fn sync(&self, force: bool) -> LmdbResult<()> { pub fn sync(&self, force: bool) -> LmdbResult<()> {
@ -123,13 +129,13 @@ impl Environment {
pub fn clear_db(&mut self, name: Option<&str>) -> LmdbResult<()> { pub fn clear_db(&mut self, name: Option<&str>) -> LmdbResult<()> {
let db = try!(self.open_db(name)); let db = try!(self.open_db(name));
let txn = try!(self.begin_write_txn()); let txn = try!(self.begin_rw_txn());
unsafe { lmdb_result(ffi::mdb_drop(txn.txn(), db.dbi(), 0)) } unsafe { lmdb_result(ffi::mdb_drop(txn.txn(), db.dbi(), 0)) }
} }
pub fn drop_db(&mut self, name: Option<&str>) -> LmdbResult<()> { pub fn drop_db(&mut self, name: Option<&str>) -> LmdbResult<()> {
let db = try!(self.open_db(name)); let db = try!(self.open_db(name));
let txn = try!(self.begin_write_txn()); let txn = try!(self.begin_rw_txn());
unsafe { lmdb_result(ffi::mdb_drop(txn.txn(), db.dbi(), 1)) } unsafe { lmdb_result(ffi::mdb_drop(txn.txn(), db.dbi(), 1)) }
} }
} }
@ -262,8 +268,8 @@ mod test {
{ // writable environment { // writable environment
let env = Environment::new().open(dir.path(), io::USER_RWX).unwrap(); let env = Environment::new().open(dir.path(), io::USER_RWX).unwrap();
assert!(env.begin_write_txn().is_ok()); assert!(env.begin_rw_txn().is_ok());
assert!(env.begin_read_txn().is_ok()); assert!(env.begin_ro_txn().is_ok());
} }
{ // read-only environment { // read-only environment
@ -271,8 +277,8 @@ mod test {
.open(dir.path(), io::USER_RWX) .open(dir.path(), io::USER_RWX)
.unwrap(); .unwrap();
assert!(env.begin_write_txn().is_err()); assert!(env.begin_rw_txn().is_err());
assert!(env.begin_read_txn().is_ok()); assert!(env.begin_ro_txn().is_ok());
} }
} }

@ -80,7 +80,7 @@ mod test_utils {
{ {
let db = env.open_db(None).unwrap(); let db = env.open_db(None).unwrap();
let mut txn = env.begin_write_txn().unwrap(); let mut txn = env.begin_rw_txn().unwrap();
for i in range(0, num_rows) { for i in range(0, num_rows) {
txn.put(db, txn.put(db,
get_key(i).as_bytes(), get_key(i).as_bytes(),

@ -70,7 +70,7 @@ pub trait TransactionExt<'env> : Transaction<'env> {
} }
/// Open a new read-only cursor on the given database. /// Open a new read-only cursor on the given database.
fn open_read_cursor<'txn>(&'txn self, db: Database) -> LmdbResult<RoCursor<'txn>> { fn open_ro_cursor<'txn>(&'txn self, db: Database) -> LmdbResult<RoCursor<'txn>> {
RoCursor::new(self, db) RoCursor::new(self, db)
} }
@ -122,6 +122,16 @@ impl <'env> RoTransaction<'env> {
} }
} }
/// Resets the read-only transaction.
///
/// Abort the transaction like `Transaction::abort`, but keep the transaction handle.
/// `InactiveTransaction::renew` may reuse the handle. This saves allocation overhead if the
/// process will start a new read-only transaction soon, and also locking overhead if
/// `EnvironmentFlags::NO_TLS` is in use. The reader table lock is released, but the table slot
/// stays tied to its thread or transaction. Reader locks generally don't interfere with
/// writers, but they keep old versions of database pages allocated. Thus they prevent the old
/// pages from being reused when writers commit new data, and so under heavy load the database
/// size may grow much more rapidly than otherwise.
pub fn reset(self) -> InactiveTransaction<'env> { pub fn reset(self) -> InactiveTransaction<'env> {
let txn = self.txn; let txn = self.txn;
unsafe { unsafe {
@ -134,7 +144,6 @@ impl <'env> RoTransaction<'env> {
_no_send: marker::NoSend, _no_send: marker::NoSend,
_contravariant: marker::ContravariantLifetime::<'env>, _contravariant: marker::ContravariantLifetime::<'env>,
} }
} }
} }
@ -161,7 +170,10 @@ impl <'env> Drop for InactiveTransaction<'env> {
impl <'env> InactiveTransaction<'env> { impl <'env> InactiveTransaction<'env> {
/// Renews the inactive transaction and returns the active read-only transaction. /// Renews the inactive transaction, returning an active read-only transaction.
///
/// This acquires a new reader lock for a transaction handle that had been released by
/// `RoTransaction::reset`.
pub fn renew(self) -> LmdbResult<RoTransaction<'env>> { pub fn renew(self) -> LmdbResult<RoTransaction<'env>> {
let txn = self.txn; let txn = self.txn;
unsafe { unsafe {
@ -213,8 +225,8 @@ impl <'env> RwTransaction<'env> {
} }
} }
/// Open a new read-write cursor on the given database. /// Opens a new read-write cursor on the given database and transaction.
pub fn open_write_cursor<'txn>(&'txn mut self, db: Database) -> LmdbResult<RwCursor<'txn>> { pub fn open_rw_cursor<'txn>(&'txn mut self, db: Database) -> LmdbResult<RwCursor<'txn>> {
RwCursor::new(self, db) RwCursor::new(self, db)
} }
@ -341,13 +353,13 @@ mod test {
let env = Environment::new().open(dir.path(), io::USER_RWX).unwrap(); let env = Environment::new().open(dir.path(), io::USER_RWX).unwrap();
let db = env.open_db(None).unwrap(); let db = env.open_db(None).unwrap();
let mut txn = env.begin_write_txn().unwrap(); let mut txn = env.begin_rw_txn().unwrap();
txn.put(db, b"key1", b"val1", WriteFlags::empty()).unwrap(); txn.put(db, b"key1", b"val1", WriteFlags::empty()).unwrap();
txn.put(db, b"key2", b"val2", WriteFlags::empty()).unwrap(); txn.put(db, b"key2", b"val2", WriteFlags::empty()).unwrap();
txn.put(db, b"key3", b"val3", WriteFlags::empty()).unwrap(); txn.put(db, b"key3", b"val3", WriteFlags::empty()).unwrap();
txn.commit().unwrap(); txn.commit().unwrap();
let mut txn = env.begin_write_txn().unwrap(); let mut txn = env.begin_rw_txn().unwrap();
assert_eq!(b"val1", txn.get(db, b"key1").unwrap()); assert_eq!(b"val1", txn.get(db, b"key1").unwrap());
assert_eq!(b"val2", txn.get(db, b"key2").unwrap()); assert_eq!(b"val2", txn.get(db, b"key2").unwrap());
assert_eq!(b"val3", txn.get(db, b"key3").unwrap()); assert_eq!(b"val3", txn.get(db, b"key3").unwrap());
@ -363,14 +375,14 @@ mod test {
let env = Environment::new().open(dir.path(), io::USER_RWX).unwrap(); let env = Environment::new().open(dir.path(), io::USER_RWX).unwrap();
let db = env.open_db(None).unwrap(); let db = env.open_db(None).unwrap();
let mut txn = env.begin_write_txn().unwrap(); let mut txn = env.begin_rw_txn().unwrap();
{ {
let mut writer = txn.reserve(db, b"key1", 4, WriteFlags::empty()).unwrap(); let mut writer = txn.reserve(db, b"key1", 4, WriteFlags::empty()).unwrap();
writer.write(b"val1").unwrap(); writer.write(b"val1").unwrap();
} }
txn.commit().unwrap(); txn.commit().unwrap();
let mut txn = env.begin_write_txn().unwrap(); let mut txn = env.begin_rw_txn().unwrap();
assert_eq!(b"val1", txn.get(db, b"key1").unwrap()); assert_eq!(b"val1", txn.get(db, b"key1").unwrap());
assert_eq!(txn.get(db, b"key"), Err(LmdbError::NotFound)); assert_eq!(txn.get(db, b"key"), Err(LmdbError::NotFound));
@ -397,7 +409,7 @@ mod test {
{ {
let db = env.open_db(None).unwrap(); let db = env.open_db(None).unwrap();
let mut txn = env.begin_write_txn().unwrap(); let mut txn = env.begin_rw_txn().unwrap();
txn.put(db, b"key", b"val", WriteFlags::empty()).unwrap(); txn.put(db, b"key", b"val", WriteFlags::empty()).unwrap();
txn.commit().unwrap(); txn.commit().unwrap();
} }
@ -405,7 +417,7 @@ mod test {
env.clear_db(None).unwrap(); env.clear_db(None).unwrap();
let db = env.open_db(None).unwrap(); let db = env.open_db(None).unwrap();
let txn = env.begin_read_txn().unwrap(); let txn = env.begin_ro_txn().unwrap();
txn.get(db, b"key").is_err(); txn.get(db, b"key").is_err();
} }
@ -416,12 +428,12 @@ mod test {
let db = env.open_db(None).unwrap(); let db = env.open_db(None).unwrap();
{ {
let mut txn = env.begin_write_txn().unwrap(); let mut txn = env.begin_rw_txn().unwrap();
txn.put(db, b"key", b"val", WriteFlags::empty()).unwrap(); txn.put(db, b"key", b"val", WriteFlags::empty()).unwrap();
txn.commit().unwrap(); txn.commit().unwrap();
} }
let txn = env.begin_read_txn().unwrap(); let txn = env.begin_ro_txn().unwrap();
let inactive = txn.reset(); let inactive = txn.reset();
let active = inactive.renew().unwrap(); let active = inactive.renew().unwrap();
assert!(active.get(db, b"key").is_ok()); assert!(active.get(db, b"key").is_ok());
@ -433,7 +445,7 @@ mod test {
let env = Environment::new().open(dir.path(), io::USER_RWX).unwrap(); let env = Environment::new().open(dir.path(), io::USER_RWX).unwrap();
let db = env.open_db(None).unwrap(); let db = env.open_db(None).unwrap();
let mut txn = env.begin_write_txn().unwrap(); let mut txn = env.begin_rw_txn().unwrap();
txn.put(db, b"key1", b"val1", WriteFlags::empty()).unwrap(); txn.put(db, b"key1", b"val1", WriteFlags::empty()).unwrap();
{ {
@ -466,21 +478,21 @@ mod test {
futures.push(Future::spawn(move|| { futures.push(Future::spawn(move|| {
let db = reader_env.open_db(None).unwrap(); let db = reader_env.open_db(None).unwrap();
{ {
let txn = reader_env.begin_read_txn().unwrap(); let txn = reader_env.begin_ro_txn().unwrap();
assert_eq!(txn.get(db, key), Err(LmdbError::NotFound)); assert_eq!(txn.get(db, key), Err(LmdbError::NotFound));
txn.abort(); txn.abort();
} }
reader_barrier.wait(); reader_barrier.wait();
reader_barrier.wait(); reader_barrier.wait();
{ {
let txn = reader_env.begin_read_txn().unwrap(); let txn = reader_env.begin_ro_txn().unwrap();
txn.get(db, key).unwrap() == val txn.get(db, key).unwrap() == val
} }
})); }));
} }
let db = env.open_db(None).unwrap(); let db = env.open_db(None).unwrap();
let mut txn = env.begin_write_txn().unwrap(); let mut txn = env.begin_rw_txn().unwrap();
barrier.wait(); barrier.wait();
txn.put(db, key, val, WriteFlags::empty()).unwrap(); txn.put(db, key, val, WriteFlags::empty()).unwrap();
txn.commit().unwrap(); txn.commit().unwrap();
@ -505,7 +517,7 @@ mod test {
futures.push(Future::spawn(move|| { futures.push(Future::spawn(move|| {
let db = writer_env.open_db(None).unwrap(); let db = writer_env.open_db(None).unwrap();
let mut txn = writer_env.begin_write_txn().unwrap(); let mut txn = writer_env.begin_rw_txn().unwrap();
txn.put(db, txn.put(db,
format!("{}{}", key, i).as_bytes(), format!("{}{}", key, i).as_bytes(),
format!("{}{}", val, i).as_bytes(), format!("{}{}", val, i).as_bytes(),
@ -517,7 +529,7 @@ mod test {
assert!(futures.iter_mut().all(|b| b.get())); assert!(futures.iter_mut().all(|b| b.get()));
let db = env.open_db(None).unwrap(); let db = env.open_db(None).unwrap();
let txn = env.begin_read_txn().unwrap(); let txn = env.begin_ro_txn().unwrap();
for i in range(0, n) { for i in range(0, n) {
assert_eq!( assert_eq!(
@ -531,7 +543,7 @@ mod test {
let n = 100u32; let n = 100u32;
let (_dir, env) = setup_bench_db(n); let (_dir, env) = setup_bench_db(n);
let db = env.open_db(None).unwrap(); let db = env.open_db(None).unwrap();
let txn = env.begin_read_txn().unwrap(); let txn = env.begin_ro_txn().unwrap();
let mut keys: Vec<String> = range(0, n).map(|n| get_key(n)) let mut keys: Vec<String> = range(0, n).map(|n| get_key(n))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@ -551,7 +563,7 @@ mod test {
let n = 100u32; let n = 100u32;
let (_dir, env) = setup_bench_db(n); let (_dir, env) = setup_bench_db(n);
let db = env.open_db(None).unwrap(); let db = env.open_db(None).unwrap();
let _txn = env.begin_read_txn().unwrap(); let _txn = env.begin_ro_txn().unwrap();
let mut keys: Vec<String> = range(0, n).map(|n| get_key(n)) let mut keys: Vec<String> = range(0, n).map(|n| get_key(n))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@ -588,7 +600,7 @@ mod test {
XorShiftRng::new_unseeded().shuffle(items.as_mut_slice()); XorShiftRng::new_unseeded().shuffle(items.as_mut_slice());
b.iter(|| { b.iter(|| {
let mut txn = env.begin_write_txn().unwrap(); let mut txn = env.begin_rw_txn().unwrap();
for &(ref key, ref data) in items.iter() { for &(ref key, ref data) in items.iter() {
txn.put(db, key.as_bytes(), data.as_bytes(), WriteFlags::empty()).unwrap(); txn.put(db, key.as_bytes(), data.as_bytes(), WriteFlags::empty()).unwrap();
} }

Loading…
Cancel
Save