Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions native/elmdb_nif/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ crate-type = ["cdylib"]
[dependencies]
rustler = { version = "0.36.2", features = ["nif_version_2_16"] }
lmdb = "0.8"
lmdb-sys = "0.8"
lazy_static = "1.4"
35 changes: 33 additions & 2 deletions native/elmdb_nif/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ use rustler::{Env, Term, NifResult, Error, Encoder, ResourceArc};
use rustler::types::binary::Binary;
use rustler::types::binary::OwnedBinary;
use std::collections::{HashMap, HashSet, VecDeque};
use std::mem;
use std::sync::{Arc, Mutex};
use std::path::Path;
use lmdb::{Environment, EnvironmentFlags, Database, DatabaseFlags, Transaction, WriteFlags, Cursor};
Expand Down Expand Up @@ -1598,15 +1599,45 @@ struct DbOptions {
#[rustler::nif]
fn env_status<'a>(env: Env<'a>, env_handle: ResourceArc<LmdbEnv>) -> NifResult<Term<'a>> {
let closed = env_handle.is_closed().map_err(|_| Error::BadArg)?;

let ref_count = {
let ref_count = env_handle.ref_count.lock().map_err(|_| Error::BadArg)?;
*ref_count
};

Ok((atoms::ok(), closed, ref_count, env_handle.path.clone()).encode(env))
}

/// Returns the configured map_size and the approximate number of bytes currently
/// used by the environment.
///
/// Used bytes are estimated as `(last_pgno + 1) * page_size`, which counts every
/// page that has ever been allocated (including free-list pages). The value is
/// therefore an upper bound on live data size, but it is the right metric for
/// deciding how close the environment is to hitting the map_size limit.
///
/// `lmdb 0.8` does not expose `mdb_env_info` in its safe API, so we call it
/// through the `lmdb-sys` FFI layer directly.
#[rustler::nif]
fn env_stat<'a>(env: Env<'a>, env_handle: ResourceArc<LmdbEnv>) -> NifResult<Term<'a>> {
let (lmdb_env, _gen) = env_handle.ensure_open().map_err(|_| Error::BadArg)?;
let stat = lmdb_env.stat().map_err(|_| Error::BadArg)?;
let page_size = stat.page_size() as usize;

// mdb_env_info is not wrapped by the `lmdb` crate, so call it directly.
let (map_size, last_pgno) = unsafe {
let mut info: lmdb_sys::MDB_envinfo = mem::zeroed();
let rc = lmdb_sys::mdb_env_info(lmdb_env.env(), &mut info);
if rc != 0 {
return Err(Error::BadArg);
}
(info.me_mapsize, info.me_last_pgno)
};

let used_bytes = (last_pgno + 1) * page_size;
Ok((atoms::ok(), map_size, used_bytes).encode(env))
}

// Initialize the NIF module
// explicit fuctions are deprecated but here is a list
// [env_open, env_close, env_close_by_name, db_open, db_close, put, put_batch, get, list, flush, env_status]
Expand Down
23 changes: 22 additions & 1 deletion src/elmdb.erl
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
-module(elmdb).

%% Environment management
-export([env_open/2, env_sync/1, env_close/1, env_close_by_name/1, env_status/1]).
-export([env_open/2, env_sync/1, env_close/1, env_close_by_name/1, env_status/1, env_stat/1]).

%% Database operations
-export([db_open/2, db_close/1]).
Expand Down Expand Up @@ -119,6 +119,27 @@ env_close_by_name(_Path) ->
env_status(_Env) ->
erlang:nif_error(nif_not_loaded).

%% @doc Get size statistics for an LMDB environment.
%%
%% Returns the configured map_size limit and an estimate of bytes currently in
%% use. Used bytes are calculated as `(last_pgno + 1) * page_size`, which
%% counts every page ever allocated (including free-list pages) and therefore
%% represents the high-water mark rather than live data only. This is the
%% correct value for gauging proximity to the map_size limit.
%%
%% Only meaningful for writable environments; calling this on a read-only
%% environment (opened with no_lock) is safe but may return stale values if
%% another process is the primary writer.
%%
%% @param Env Environment handle from env_open
%% @returns {ok, MapSize, UsedBytes} where both values are non-negative integers
%% in bytes, or {error, badarg} if the environment cannot be opened.
-spec env_stat(Env :: term()) ->
{ok, MapSize :: non_neg_integer(), UsedBytes :: non_neg_integer()} |
{error, badarg}.
env_stat(_Env) ->
erlang:nif_error(nif_not_loaded).


%%%===================================================================
%%% Database Operations
Expand Down