Add freelist to environment (#41)

without.crypto
Nan Jiang 6 years ago committed by GitHub
parent 9996feebe0
commit d62da60df5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      Cargo.toml
  2. 6
      src/database.rs
  3. 77
      src/environment.rs
  4. 5
      src/lib.rs

@ -35,10 +35,10 @@ members = [
[dependencies]
bitflags = "1"
byteorder = "1.0"
libc = "0.2"
lmdb-rkv-sys = "0.8.2"
[dev-dependencies]
rand = "0.4"
tempdir = "0.3"
byteorder = "1.0"

@ -31,6 +31,12 @@ impl Database {
Ok(Database { dbi: dbi })
}
pub(crate) fn freelist_db() -> Database {
Database {
dbi: 0,
}
}
/// Returns the underlying LMDB database handle.
///
/// The caller **must** ensure that the handle is not used after the lifetime of the

@ -10,7 +10,10 @@ use std::sync::Mutex;
use ffi;
use error::{Result, lmdb_result};
use byteorder::{ByteOrder, NativeEndian};
use cursor::Cursor;
use error::{Error, Result, lmdb_result};
use database::Database;
use transaction::{RoTransaction, RwTransaction, Transaction};
use flags::{DatabaseFlags, EnvironmentFlags};
@ -173,6 +176,52 @@ impl Environment {
}
}
/// Retrieves the total number of pages on the freelist.
///
/// Along with `Environment::info()`, this can be used to calculate the exact number
/// of used pages as well as free pages in this environment.
///
/// ```ignore
/// let env = Environment::new().open("/tmp/test").unwrap();
/// let info = env.info().unwrap();
/// let stat = env.stat().unwrap();
/// let freelist = env.freelist().unwrap();
/// let last_pgno = info.last_pgno() + 1; // pgno is 0 based.
/// let total_pgs = info.map_size() / stat.page_size() as usize;
/// let pgs_in_use = last_pgno - freelist;
/// let pgs_free = total_pgs - pgs_in_use;
/// ```
///
/// Note:
///
/// * LMDB stores all the freelists in the designated database 0 in each environment,
/// and the freelist count is stored at the beginning of the value as `libc::size_t`
/// in the native byte order.
///
/// * It will create a read transaction to traverse the freelist database.
pub fn freelist(&self) -> Result<size_t> {
let mut freelist: size_t = 0;
let db = Database::freelist_db();
let txn = self.begin_ro_txn()?;
let mut cursor = txn.open_ro_cursor(db)?;
for result in cursor.iter() {
let (_key, value) = result?;
if value.len() < mem::size_of::<size_t>() {
return Err(Error::Corrupted);
}
let s = &value[..mem::size_of::<size_t>()];
if cfg!(target_pointer_width = "64") {
freelist += NativeEndian::read_u64(s) as size_t;
} else {
freelist += NativeEndian::read_u32(s) as size_t;
}
}
Ok(freelist)
}
/// Sets the size of the memory map to use for the environment.
///
/// This could be used to resize the map when the environment is already open.
@ -549,6 +598,32 @@ mod test {
assert_eq!(info.num_readers(), 0);
}
#[test]
fn test_freelist() {
let dir = TempDir::new("test").unwrap();
let env = Environment::new().open(dir.path()).unwrap();
let db = env.open_db(None).unwrap();
let mut freelist = env.freelist().unwrap();
assert_eq!(freelist, 0);
// Write a few small values.
for i in 0..64 {
let mut value = [0u8; 8];
LittleEndian::write_u64(&mut value, i);
let mut tx = env.begin_rw_txn().expect("begin_rw_txn");
tx.put(db, &value, &value, WriteFlags::default()).expect("tx.put");
tx.commit().expect("tx.commit")
}
let mut tx = env.begin_rw_txn().expect("begin_rw_txn");
tx.clear_db(db).expect("clear");
tx.commit().expect("tx.commit");
// Freelist should not be empty after clear_db.
freelist = env.freelist().unwrap();
assert!(freelist > 0);
}
#[test]
fn test_set_map_size() {
let dir = TempDir::new("test").unwrap();

@ -7,6 +7,7 @@
extern crate libc;
extern crate lmdb_rkv_sys as ffi;
extern crate byteorder;
#[cfg(test)] extern crate rand;
#[cfg(test)] extern crate tempdir;
@ -67,9 +68,7 @@ mod transaction;
#[cfg(test)]
mod test_utils {
extern crate byteorder;
use self::byteorder::{ByteOrder, LittleEndian};
use byteorder::{ByteOrder, LittleEndian};
use tempdir::TempDir;
use super::*;

Loading…
Cancel
Save