From c49213cbe64fd8bfa922221c73502a3b1765ef77 Mon Sep 17 00:00:00 2001 From: Gregory Burd Date: Tue, 21 May 2013 07:32:34 -0400 Subject: [PATCH] WIP: slowly adding more LMDB API on the C/NIF side --- c_src/common.h | 4 +- c_src/lmdb_nif.c | 390 +++++++++++++++++++++++++++++++++++++++-------- src/lmdb.erl | 101 ++++++------ 3 files changed, 383 insertions(+), 112 deletions(-) diff --git a/c_src/common.h b/c_src/common.h index bbb4fdd..e36c5ef 100644 --- a/c_src/common.h +++ b/c_src/common.h @@ -49,8 +49,8 @@ extern "C" { } while (0) #endif -#ifndef __UNUSED -#define __UNUSED(v) ((void)(v)) +#ifndef UNUSED +#define UNUSED(v) ((void)(v)) #endif diff --git a/c_src/lmdb_nif.c b/c_src/lmdb_nif.c index ec4ca8b..1d86c5d 100644 --- a/c_src/lmdb_nif.c +++ b/c_src/lmdb_nif.c @@ -2,7 +2,7 @@ * This file is part of LMDB - Erlang Lightning MDB API * * Copyright (c) 2012 by Aleph Archives. All rights reserved. -%% Copyright (c) 2013 by Basho Technologies, Inc. All rights reserved. + * Copyright (c) 2013 by Basho Technologies, Inc. All rights reserved. * * ------------------------------------------------------------------------- * Redistribution and use in source and binary forms, with or without @@ -38,23 +38,41 @@ #include "stats.h" #include "lmdb.h" -STAT_DECL(lmdb_get, 1000); -STAT_DECL(lmdb_put, 1000); -STAT_DECL(lmdb_del, 1000); -STAT_DECL(lmdb_upd, 1000); +STAT_DECL(lmdb_env_get, 1000); +STAT_DECL(lmdb_env_put, 1000); +STAT_DECL(lmdb_env_del, 1000); +STAT_DECL(lmdb_env_upd, 1000); -static ErlNifResourceType *lmdb_RESOURCE; -struct lmdb { +static ErlNifResourceType *lmdb_env_RESOURCE; +struct lmdb_env { MDB_env *env; - MDB_dbi dbi; - STAT_DEF(lmdb_get); - STAT_DEF(lmdb_put); - STAT_DEF(lmdb_del); - STAT_DEF(lmdb_upd); + STAT_DEF(lmdb_env_get); + STAT_DEF(lmdb_env_put); + STAT_DEF(lmdb_env_del); + STAT_DEF(lmdb_env_upd); }; +static ErlNifResourceType *lmdb_dbi_RESOURCE; +struct lmdb_dbi { + MDB_dbi dbi; +}; + +static ErlNifResourceType *lmdb_txn_RESOURCE; +struct lmdb_txn { + MDB_txn *txn; +}; + +static ErlNifResourceType *lmdb_cursor_RESOURCE; +struct lmdb_cursor { + MDB_cursor *cursor; +}; + +KHASH_MAP_INIT_PTR(envs, struct lmdb_env*); + struct lmdb_priv_data { void *async_nif_priv; // Note: must be first element in struct + khash_t(envs) *envs; // TODO: could just be a list + ErlNifMutex *envs_mutex; }; /* Global init for async_nif. */ @@ -175,6 +193,190 @@ __strerror_term(ErlNifEnv* env, int err) enif_make_string(env, mdb_strerror(err), ERL_NIF_LATIN1))); } +/** + * Opens an MDB environment + * + * argv[0] path to directory for the database files + * argv[1] flags + * argv[2] file mode + * argv[3] mapsize + * argv[4] maxreaders + * argv[5] maxdbs + */ +ASYNC_NIF_DECL( + lmdb_env_open_nif, + { // struct + + char dirname[MAXPATHLEN]; + unsigned int flags; + mdb_mod_t mode; + size_t mapsize; + unsigned int maxreaders; + MDB_dbi maxdbs; + struct wterl_priv_data *priv; + }, + { // pre + + if (!(argc == 6 && + enif_is_list(env, argv[0]) && + enif_is_number(env, argv[1]) && + enif_is_number(env, argv[2]) && + enif_is_number(env, argv[3]) && + enif_is_number(env, argv[4]) && + enif_is_number(env, argv[5]))) { + ASYNC_NIF_RETURN_BADARG(); + } + if (enif_get_string(env, argv[0], args->dirname, MAXPATHLEN, ERL_NIF_LATIN1) <= 0) + ASYNC_NIF_RETURN_BADARG(); + enif_get_uint32(env, argv[1], &(args->flags)); + enif_get_int32(env, argv[2], &(args->mode)); +#if (__SIZEOF_SIZE_T__ == 8) + enif_get_int64(env, argv[3], &(args->mapsize)); +#else if (__SIZEOF_SIZE_T__ == 4) + enif_get_int32(env, argv[3], &(args->mapsize)); +#endif + enif_get_int32(env, argv[4], &(args->maxreaders)); + enif_get_int32(env, argv[5], &(args->maxdbs)); + args->priv = (struct lmdb_priv_data *)enif_priv_data(env); + }, + { // work + + int ret; + ERL_NIF_TERM err; + struct lmdb_env *handle; + khash_t(envs) *h; + khiter_t itr; + int itr_status; + + if ((handle = enif_alloc_resource(lmdb_RESOURCE, sizeof(struct lmdb))) == NULL) + FAIL_ERR(ENOMEM, err2); + + STAT_INIT(handle, lmdb_env_get); + STAT_INIT(handle, lmdb_env_put); + STAT_INIT(handle, lmdb_env_upd); + STAT_INIT(handle, lmdb_env_del); + + CHECK(mdb_env_create(&(handle->env)), err1); + + if (mdb_env_set_mapsize(handle->env, args->mapsize)) { + err = enif_make_badarg(handle->env); + goto err1; + } + + if (mdb_env_set_maxreaders(handle->env, args->maxreaders)) { + err = enif_make_badarg(handle->env); + goto err1; + } + + if (mdb_env_set_maxdbs(handle->env, args->maxdbs)) { + err = enif_make_badarg(handle->env); + goto err1; + } + + h = args->priv->envs; + itr = kh_put(envs, h, handle, &itr_status); + kh_value(h, itr) = handle; + + ERL_NIF_TERM term = enif_make_resource(env, handle); + ASYNC_NIF_REPLY(enif_make_tuple(env, 2, ATOM_OK, term)); + enif_release_resource(handle); + return; + + err1: + mdb_env_close(handle->env); + err2: + enif_release_resource(handle); + ASYNC_NIF_REPLY(err); + return; + }, + { // post + + }); + +/** + * Copy an MDB environment to the specified path. + * + * argv[0] an environment handle + * argv[1] destination path + */ +ASYNC_NIF_DECL( + lmdb_copy_nif, + { // struct + + struct lmdb_env *handle; + char dirname[MAXPATHLEN]; + }, + { // pre + + if (!(argc == 2 && + enif_get_resource(env, argv[0], lmdb_RESOURCE, (void**)&args->handle) && + enif_is_list(env, arg[1])) { + ASYNC_NIF_RETURN_BADARG(); + } + if (enif_get_string(env, argv[1], args->dirname, MAXPATHLEN, + ERL_NIF_LATIN1) <= 0) + ASYNC_NIF_RETURN_BADARG(); + if (!args->handle->env) + ASYNC_NIF_RETURN_BADARG(); + enif_keep_resource((void*)args->handle); + }, + { // work + + ERL_NIF_TERM err; + int ret; + + CHECK(mdb_env_copy(args->handle->env, args->dirname, err)); + ASYNC_NIF_REPLY(ATOM_OK); + return; + + err: + ASYNC_NIF_REPLY(err); + return; + }, + { // post + + enif_release_resource((void*)args->handle); + }). + +/** + * ?? + * + * argv[0] ?? + */ +ASYNC_NIF_DECL( + lmdb_??_nif, + { // struct + + struct lmdb *handle; + }, + { // pre + + if (!(argc == 1 && + enif_get_resource(env, argv[0], lmdb_RESOURCE, (void**)&args->handle))) { + ASYNC_NIF_RETURN_BADARG(); + } + if (!args->handle->env) + ASYNC_NIF_RETURN_BADARG(); + enif_keep_resource((void*)args->handle); + }, + { // work + + ERL_NIF_TERM err; + int ret; + + CHECK(??, err); + ASYNC_NIF_REPLY(enif_make_tuple(env, 2, ATOM_OK, term)); + return; + + err: + ASYNC_NIF_REPLY(err); + return; + }, + { // post + + enif_release_resource((void*)args->handle); + }). + /** * Opens a MDB database. * @@ -195,6 +397,7 @@ ASYNC_NIF_DECL( ErlNifUInt64 envflags; }, { // pre + if (!(argc == 3 && enif_is_list(env, argv[0]) && enif_is_number(env, argv[1]) && @@ -216,6 +419,7 @@ ASYNC_NIF_DECL( if ((handle = enif_alloc_resource(lmdb_RESOURCE, sizeof(struct lmdb))) == NULL) FAIL_ERR(ENOMEM, err3); + enif_release_resource(handle); STAT_INIT(handle, lmdb_get); STAT_INIT(handle, lmdb_put); @@ -235,7 +439,6 @@ ASYNC_NIF_DECL( CHECK(mdb_txn_commit(txn), err1); ERL_NIF_TERM term = enif_make_resource(env, handle); - enif_release_resource(handle); ASYNC_NIF_REPLY(enif_make_tuple(env, 2, ATOM_OK, term)); return; @@ -244,6 +447,7 @@ ASYNC_NIF_DECL( err2: mdb_env_close(handle->env); err3: + enif_release_resource(handle); ASYNC_NIF_REPLY(err); return; }, @@ -642,23 +846,38 @@ ASYNC_NIF_DECL( }); - -static int lmdb_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) +/** + * Called as this driver is loaded by the Erlang BEAM runtime triggered by the + * module's on_load directive. + * + * env the NIF environment + * priv_data used to hold the state for this NIF rather than global variables + * load_info an Erlang term passed in with this call + */ +static int +lmdb_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) { - __UNUSED(load_info); + UNUSED(load_info); + int err; + char msg[1024]; + ErlNifResourceFlags flags; + struct lmdb_priv_data *priv; - ErlNifResourceFlags flags = ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER; - - struct lmdb_priv_data *priv = enif_alloc(sizeof(struct lmdb_priv_data)); + flags = ERL_NIF_RT_CREATE | ERL_NIF_RT_TAKEOVER; + priv = enif_alloc(sizeof(struct lmdb_priv_data)); if (!priv) - return ENOMEM; + FAIL_ERR(ENOMEM, err1); memset(priv, 0, sizeof(struct lmdb_priv_data)); + priv->envs_mutex = enif_mutex_create(NULL); + priv->envs = kh_init(envs); + if (!priv->envs) + FAIL_ERR(ENOMEM, err2); /* Note: !!! the first element of our priv_data struct *must* be the pointer to the async_nif's private data which we set here. */ ASYNC_NIF_LOAD(lmdb, priv->async_nif_priv); if (!priv) - return ENOMEM; + FAIL_ERR(ENOMEM, err3); *priv_data = priv; ATOM_ERROR = enif_make_atom(env, "error"); @@ -684,68 +903,111 @@ static int lmdb_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) lmdb_RESOURCE = enif_open_resource_type(env, NULL, "lmdb_resource", NULL, flags, NULL); + fprintf(stderr, "NIF on_load complete (lmdb version: %s)", MDB_VERSION_STRING); + fflush(stderr); return (0); -} -static int lmdb_reload(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM info) -{ - __UNUSED(env); - __UNUSED(priv_data); - __UNUSED(info); - return (0); // TODO: +err3: + kh_destroy(envs, priv->envs); +err2: + enif_mutex_destroy(priv->conns_mutex); + enif_free(priv); +err1: + return (ENOMEM); } -static int lmdb_upgrade(ErlNifEnv* env, void** priv_data, void** old_priv, ERL_NIF_TERM load_info) +/** + * TODO: + */ +static int +lmdb_reload(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM info) { - __UNUSED(env); - __UNUSED(priv_data); - __UNUSED(old_priv); - __UNUSED(load_info); + UNUSED(env); + UNUSED(priv_data); + UNUSED(info); + return (0); // TODO: implement +} + + +/** + * TODO: + */ +static int +lmdb_upgrade(ErlNifEnv* env, void** priv_data, void** old_priv, ERL_NIF_TERM load_info) +{ + UNUSED(env); + UNUSED(priv_data); + UNUSED(old_priv); + UNUSED(load_info); ASYNC_NIF_UPGRADE(lmdb, env); return (0); // TODO: } -static void lmdb_unload(ErlNifEnv* env, void* priv_data) +/** + * TODO: + */ +static void +lmdb_unload(ErlNifEnv* env, void* priv_data) { struct lmdb_priv_data *priv = (struct lmdb_priv_data *)priv_data; + khash_t(envs) *h; + khiter_t itr_envs; + struct lmdb_env *env; + + enif_mutex_lock(priv->envs_mutex); + h = priv->envs; + for (itr_envs = kh_begin(h); itr_envs != kh_end(h); ++itr_envs) { + if (kh_exist(h, itr_envs)) { + env = kh_val(h, itr_envs); + if (env) { + mdb_env_close(env); + kh_del(envs, h, itr_envs); + enif_free(env); + kh_value(h, itr_envs) = NULL; + } + } + } + kh_destroy(envs, h); ASYNC_NIF_UNLOAD(lmdb, env, priv->async_nif_priv); + enif_mutex_unlock(priv->envs_mutex); + enif_mutex_destroy(priv->envs_mutex); enif_free(priv); return; } static ErlNifFunc nif_funcs [] = { - {"env_open" 4, lmdb_env_open}, // [(Ref), Path, Bitmask, Mode] - {"copy", 3, lmdb_copy}, // [(Ref), Env, Path] - {"stat", 2, lmdb_stat}, // [(Ref), Env] - {"sync", 3, lmdb_sync}, // [(Ref), Env, Force] - {"env_close", 2, lmdb_env_close}, // [(Ref), Env] - {"path", 2, lmdb_path}, // [(Ref), Env] - {"set_maxdbs", 3, lmdb_set_maxdbs}, // [(Ref), Env, Dbs] - {"txn_begin", 4, lmdb_txn_begin}, // [(Ref), Env, Parent, Bitmask]); - {"txn_commit", 2, lmdb_txn_commit}, // [(Ref), Txn] - {"txn_abort", 2, lmdb_txn_abort}, // [(Ref), Txn] - {"txn_reset", 2, lmdb_txn_reset}, // [(Ref), Txn] - {"txn_renew", 2, lmdb_txn_renew}, // [(Ref), Txn] - {"dbi_open", 5, lmdb_dbi_open}, // [(Ref), Txn, Path, Size, Bitmask] - {"dbi_close", 2, lmdb_dbi_close}, // [(Ref), Env, Dbi] - {"drop", 4, lmdb_drop}, // [(Ref), Txn, Dbi, Delete] - {"get", 3, lmdb_get}, // [(Ref), Txn, Dbi, Key] - {"put", 4, lmdb_put}, // [(Ref), Txn, Dbi, Key, Value, Bitmask] - {"del", 5, lmdb_del}, // [(Ref), Txn, Dbi, Key, Value] - {"cursor_open", 3, lmdb_cursor_open}, // [(Ref), Txn, Dbi] - {"cursor_close", 2, lmdb_cursor_close}, // [(Ref), Cursor] - {"cursor_renew", 3, lmdb_cursor_renew}, // [(Ref), Txn, Cursor] - {"cursor_txn", 2, lmdb_cursor_txn}, // [(Ref), Cursor] - {"cursor_dbi", 2, lmdb_cursor_dbi}, // [(Ref), Cursor] - {"cursor_get", 5, lmdb_cursor_get}, // [(Ref), Cursor, Key, DupValue, CursorOp] - {"cursor_put", 5, lmdb_cursor_put}, // [(Ref), Cursor, Key, Value, Options] - {"cursor_del", 3, lmdb_cursor_del}, // [(Ref), Cursor, Bitmask] - {"cursor_count", 2, lmdb_cursor_count}, // [(Ref), Cursor] - {"cmp", 5, lmdb_cmp}, // [(Ref), Txn, Dbi, A, B] - {"dup_cmp", 5, lmdb_dup_cmp}, // [(Ref), Txn, Dbi, A, B] - {"version", 1, lmdb_version} // [(Ref)] + {"env_open_nif", 7, lmdb_env_open}, // [(Ref), Path, Bitmask, Mode] + {"copy_nif", 3, lmdb_copy}, // [(Ref), Env, Path] + {"stat_nif", 2, lmdb_stat}, // [(Ref), Env] + {"sync_nif", 3, lmdb_sync}, // [(Ref), Env, Force] + {"env_close_nif", 2, lmdb_env_close}, // [(Ref), Env] + {"path_nif", 2, lmdb_path}, // [(Ref), Env] + {"set_maxdbs_nif", 3, lmdb_set_maxdbs}, // [(Ref), Env, Dbs] + {"txn_begin_nif", 4, lmdb_txn_begin}, // [(Ref), Env, Parent, Bitmask]); + {"txn_commit_nif", 2, lmdb_txn_commit}, // [(Ref), Txn] + {"txn_abort_nif", 2, lmdb_txn_abort}, // [(Ref), Txn] + {"txn_reset_nif", 2, lmdb_txn_reset}, // [(Ref), Txn] + {"txn_renew_nif", 2, lmdb_txn_renew}, // [(Ref), Txn] + {"dbi_open_nif", 5, lmdb_dbi_open}, // [(Ref), Txn, Path, Size, Bitmask] + {"dbi_close_nif", 2, lmdb_dbi_close}, // [(Ref), Env, Dbi] + {"drop_nif", 4, lmdb_drop}, // [(Ref), Txn, Dbi, Delete] + {"get_nif", 3, lmdb_get}, // [(Ref), Txn, Dbi, Key] + {"put_nif", 4, lmdb_put}, // [(Ref), Txn, Dbi, Key, Value, Bitmask] + {"del_nif", 5, lmdb_del}, // [(Ref), Txn, Dbi, Key, Value] + {"cursor_open_nif", 3, lmdb_cursor_open}, // [(Ref), Txn, Dbi] + {"cursor_close_nif", 2, lmdb_cursor_close}, // [(Ref), Cursor] + {"cursor_renew_nif", 3, lmdb_cursor_renew}, // [(Ref), Txn, Cursor] + {"cursor_txn_nif", 2, lmdb_cursor_txn}, // [(Ref), Cursor] + {"cursor_dbi_nif", 2, lmdb_cursor_dbi}, // [(Ref), Cursor] + {"cursor_get_nif", 5, lmdb_cursor_get}, // [(Ref), Cursor, Key, DupValue, CursorOp] + {"cursor_put_nif", 5, lmdb_cursor_put}, // [(Ref), Cursor, Key, Value, Options] + {"cursor_del_nif", 3, lmdb_cursor_del}, // [(Ref), Cursor, Bitmask] + {"cursor_count_nif", 2, lmdb_cursor_count}, // [(Ref), Cursor] + {"cmp_nif", 5, lmdb_cmp}, // [(Ref), Txn, Dbi, A, B] + {"dup_cmp_nif", 5, lmdb_dup_cmp}, // [(Ref), Txn, Dbi, A, B] + {"version_nif", 1, lmdb_version} // [(Ref)] }; /* driver entry point */ diff --git a/src/lmdb.erl b/src/lmdb.erl index ef068e8..8b8ca2e 100644 --- a/src/lmdb.erl +++ b/src/lmdb.erl @@ -33,7 +33,7 @@ %%==================================================================== %% Public API: --export([env_open/3, +-export([env_open/3, env_open/6, env_close/1, copy/2, stat/1, @@ -131,7 +131,7 @@ prev | prev_dup | prev_multiple | prev_nodup | set | set_key | set_range. -type path() :: string(). --type mode() :: string(). +-type mode() :: non_neg_integer(). -type byte_size() :: non_neg_integer() | { non_neg_integer(), b|bytes|'GB'|'GiB'|'TB'|'TiB'|'PB'|'PiB' }. @@ -145,59 +145,62 @@ %% If this function fails, env_close() must be called to discard the %% reference. %% -%% path The directory in which the database files reside. This -%% directory must already exist and be writable. +%% path The directory in which the database files reside. This +%% directory must already exist and be writable. %% -%% flags Special options for this environment. This parameter -%% must be set to zero or more of the values described here. +%% flags Special options for this environment. This parameter +%% must be set to zero or more of the values described here. %% %% fixedmap - Use a fixed address for the mmap region. %% nosubdir - By default, LMDB creates its environment in a directory whose -%% pathname is given in \b path, and creates its data and lock files -%% under that directory. With this option, \b path is used as-is for -%% the database main data file. The database lock file is the \b path -%% with "-lock" appended. +%% pathname is given in \b path, and creates its data and lock files +%% under that directory. With this option, \b path is used as-is for +%% the database main data file. The database lock file is the \b path +%% with "-lock" appended. %% rdonly - Open the environment in read-only mode. No write operations will be -%% allowed. LMDB will still modify the lock file - except on read-only -%% filesystems, where LMDB does not use locks. +%% allowed. LMDB will still modify the lock file - except on read-only +%% filesystems, where LMDB does not use locks. %% writemap - Use a writeable memory map unless MDB_RDONLY is set. This is faster -%% and uses fewer mallocs, but loses protection from application bugs -%% like wild pointer writes and other bad updates into the database. -%% Incompatible with nested transactions. +%% and uses fewer mallocs, but loses protection from application bugs +%% like wild pointer writes and other bad updates into the database. +%% Incompatible with nested transactions. %% nometasync - Flush system buffers to disk only once per transaction, omit the -%% metadata flush. Defer that until the system flushes files to disk, -%% or next non-LMDB_RDONLY commit or sync(). This optimization -%% maintains database integrity, but a system crash may undo the last -%% one or more committed transaction. I.e. this option preserves the -%% ACI (atomicity, consistency, isolation) but not D (durability) -%% database transaction properties. +%% metadata flush. Defer that until the system flushes files to disk, +%% or next non-LMDB_RDONLY commit or sync(). This optimization +%% maintains database integrity, but a system crash may undo the last +%% one or more committed transaction. I.e. this option preserves the +%% ACI (atomicity, consistency, isolation) but not D (durability) +%% database transaction properties. %% nosync - Don't flush system buffers to disk when committing a transaction. -%% This optimization means a system crash can corrupt the database or -%% lose the last transactions if buffers are not yet flushed to disk. -%% The risk is governed by how often the system flushes dirty buffers -%% to disk and how often sync() is called. However, if the filesystem -%% preserves write order and the `writemap` flag is not used, -%% transactions exhibit ACI (atomicity, consistency, isolation) -%% properties and only lose D (durability). I.e. database integrity -%% is maintained, but a system crash may undo the final transactions. -%% Note that using [nosync, writemap] togher leaves the system with no -%% hint for when to write transactions to disk, unless sync() -%% is called. [mapsync, writemap] may be preferable. +%% This optimization means a system crash can corrupt the database or +%% lose the last transactions if buffers are not yet flushed to disk. +%% The risk is governed by how often the system flushes dirty buffers +%% to disk and how often sync() is called. However, if the filesystem +%% preserves write order and the `writemap` flag is not used, +%% transactions exhibit ACI (atomicity, consistency, isolation) +%% properties and only lose D (durability). I.e. database integrity +%% is maintained, but a system crash may undo the final transactions. +%% Note that using [nosync, writemap] togher leaves the system with no +%% hint for when to write transactions to disk, unless sync() +%% is called. [mapsync, writemap] may be preferable. %% mapsync - When using `writemap`, use asynchronous flushes to disk. -%% As with `nosync`, a system crash can then corrupt the database -%% or lose the last transactions. Calling sync() ensures on-disk -%% database integrity until next commit. +%% As with `nosync`, a system crash can then corrupt the database +%% or lose the last transactions. Calling sync() ensures on-disk +%% database integrity until next commit. %% notls - Don't use Thread-Local Storage. Tie reader locktable slots to -%% txn objects instead of to threads. I.e. txn_reset() keeps the -%% slot reseved for the txn object. A thread may use parallel -%% read-only transactions. A read-only transaction may span threads if -%% the user synchronizes its use. Applications that multiplex many -%% user threads over individual OS threads need this option. Such an -%% application must also serialize the write transactions in an OS -%% thread, since MDB's write locking is unaware of the user threads. +%% txn objects instead of to threads. I.e. txn_reset() keeps the +%% slot reseved for the txn object. A thread may use parallel +%% read-only transactions. A read-only transaction may span threads if +%% the user synchronizes its use. Applications that multiplex many +%% user threads over individual OS threads need this option. Such an +%% application must also serialize the write transactions in an OS +%% thread, since MDB's write locking is unaware of the user threads. %% -%% mode The UNIX permissions to set on created files. This parameter -%% is ignored on Windows. +%% mode The UNIX permissions to set on created files. This parameter +%% is ignored on Windows. +%% mapsize +%% maxreaders +%% maxdbs %% %% Possible errors are: %% MDB_VERSION_MISMATCH - the version of the MDB library doesn't match the @@ -210,7 +213,11 @@ %%-------------------------------------------------------------------- -spec env_open(path(), options(), mode()) -> {ok, env()} | {error, term()}. +-spec env_open(path(), options(), mode(), non_neg_integer(), + non_neg_integer(), non_neg_integer()) -> {ok, env()} | {error, term()}. env_open(Path, Options, Mode) -> + env_open(Path, Options, Mode, {2, 'GB'}, 1000, 100). % guess at default values +env_open(Path, Options, Mode, MapSize, MaxReaders, MaxDbs) -> ValidOptions = [{ fixedmap, ?MDB_FIXEDMAP, []}, { nosubdir, ?MDB_NOSUBDIR, []}, { rdonly, ?MDB_RDONLY, [writemap]}, @@ -223,12 +230,14 @@ env_open(Path, Options, Mode) -> {ok, Bitmask} -> %% Ensure directory exists ok = filelib:ensure_dir(filename:join([Path, "x"])), - ?ASYNC_NIF_CALL(fun env_open_nif/4, [Path, Bitmask, Mode]); + ?ASYNC_NIF_CALL(fun env_open_nif/7, [Path, Bitmask, Mode, + in_bytes(MapSize), MaxReaders, + MaxDbs]); {error, _Reason}=Error -> Error end. -env_open_nif(_AsyncRef, _Path, _Options, _Mode) -> +env_open_nif(_AsyncRef, _Path, _Options, _Mode, _MapSize, _MaxReaders, _MaxDbs) -> ?NOT_LOADED. %%--------------------------------------------------------------------