add open/2 and open/3

This commit is contained in:
younes 2012-09-30 22:16:46 +02:00
parent 1a33be0432
commit ebb69cd53d
6 changed files with 186 additions and 18 deletions

View file

@ -12,7 +12,12 @@ But this module could also be used as a general key-value store to replace:
* Erlang R14B04+ * GCC 4.2+ or MS VisualStudio 2010+ Build ----- $ make API --- * Erlang R14B04+ * GCC 4.2+ or MS VisualStudio 2010+ Build ----- $ make API ---
The following functions were implemented: The following functions were implemented:
* `open/1`: creates a new MDB database. This call also re-open an already existing one. * `open/1`: equivalent to `emdb:open(DirName, 10485760)`.
* `open/1`: equivalent to `emdb:open(DirName, 10485760, 0)`.
* `open/3`: creates a new MDB database. This call also re-open an already existing one. Arguments are:
* DirName: database directory name
* MapSize: database map size (see [map.hrl](http://gitorious.org/mdb/mdb/blobs/master/libraries/libmdb/mdb.h))
* EnvFlags: database environment flags (see [map.hrl](http://gitorious.org/mdb/mdb/blobs/master/libraries/libmdb/mdb.h)). The possible values are defined in **emdb.hrl**.
* `close/2`: closes the database * `close/2`: closes the database
* `put/2`: inserts Key with value Val into the database. Assumes that the key is not present, 'key_exit' is returned otherwise. * `put/2`: inserts Key with value Val into the database. Assumes that the key is not present, 'key_exit' is returned otherwise.
* `get/1`: retrieves the value stored with Key in the database. * `get/1`: retrieves the value stored with Key in the database.
@ -58,9 +63,15 @@ $ ./start.sh
%% close the database 16> ok = Handle:close(). %% close the database 16> ok = Handle:close().
...
17> q(). 17> q().
####Note:
This code below create a new database with **80GB** MapSize, **avoid fsync**
after each commit (for max speed) and use the experimental **MDB_FIXEDMAP**. {ok, Handle} = emdb:open("/tmp/emdb2", 85899345920, ?MDB_NOSYNC bor ?MDB_FIXEDMAP).
Performance ----------- For maximum speed, this library use only binaries for both keys and values. Performance ----------- For maximum speed, this library use only binaries for both keys and values.
See the impressive [microbench](http://highlandsun.com/hyc/mdb/microbench/) against: See the impressive [microbench](http://highlandsun.com/hyc/mdb/microbench/) against:

View file

@ -65,6 +65,7 @@ static struct emdb_map_t * emdb_map = NULL;
/* emdb errors */ /* emdb errors */
#define EMDB_MALLOC_ERR "error_malloc" #define EMDB_MALLOC_ERR "error_malloc"
#define EMDB_MAKE_BINARY_ERR "error_make_binary"
#define EMDB_CREATE_ERR "error_create" #define EMDB_CREATE_ERR "error_create"
#define EMDB_MAPSIZE_ERR "error_mapsize" #define EMDB_MAPSIZE_ERR "error_mapsize"
#define EMDB_OPEN_ERR "error_open" #define EMDB_OPEN_ERR "error_open"
@ -100,20 +101,28 @@ static ERL_NIF_TERM emdb_open_nif (ErlNifEnv * env,
struct emdb_map_t * node; struct emdb_map_t * node;
MDB_txn * txn; MDB_txn * txn;
char * err; char * err;
ErlNifUInt64 mapsize;
ErlNifUInt64 envflags;
if (enif_get_string(env, argv[0], dirname, MAXPATHLEN, ERL_NIF_LATIN1) <= 0) if (enif_get_string(env, argv[0], dirname, MAXPATHLEN, ERL_NIF_LATIN1) <= 0)
return enif_make_badarg(env); return enif_make_badarg(env);
if(! (node = calloc(1, sizeof(struct emdb_map_t)))) if(! (node = calloc(1, sizeof(struct emdb_map_t))))
FAIL_FAST(EMDB_MALLOC_ERR, err3); FAIL_FAST(EMDB_MALLOC_ERR, err3);
if (mdb_env_create(& (node -> env))) if (mdb_env_create(& (node -> env)))
FAIL_FAST(EMDB_CREATE_ERR, err2); FAIL_FAST(EMDB_CREATE_ERR, err2);
if (mdb_env_set_mapsize(node -> env, 10485760)) if (! enif_get_uint64(env, argv[1], & mapsize))
return enif_make_badarg(env);
if (mdb_env_set_mapsize(node -> env, mapsize))
FAIL_FAST(EMDB_MAPSIZE_ERR, err2); FAIL_FAST(EMDB_MAPSIZE_ERR, err2);
if (mdb_env_open(node -> env, dirname, MDB_FIXEDMAP, 0664)) if (! enif_get_uint64(env, argv[2], & envflags))
return enif_make_badarg(env);
if (mdb_env_open(node -> env, dirname, envflags, 0664))
FAIL_FAST(EMDB_OPEN_ERR, err2); FAIL_FAST(EMDB_OPEN_ERR, err2);
if (mdb_txn_begin(node -> env, NULL, 0, & txn)) if (mdb_txn_begin(node -> env, NULL, 0, & txn))
@ -209,7 +218,7 @@ static ERL_NIF_TERM emdb_put_nif (ErlNifEnv * env,
ret = mdb_put(txn, node -> dbi, & mkey, & mdata, MDB_NOOVERWRITE); ret = mdb_put(txn, node -> dbi, & mkey, & mdata, MDB_NOOVERWRITE);
if (MDB_KEYEXIST == ret) if (MDB_KEYEXIST == ret)
FAIL_FAST(EMDB_RET_KEY_EXIST, err1); FAIL_FAST(EMDB_RET_KEY_EXIST, err1);
if (ret) if (ret)
FAIL_FAST(EMDB_PUT_ERR, err1); FAIL_FAST(EMDB_PUT_ERR, err1);
if (mdb_txn_commit(txn)) if (mdb_txn_commit(txn))
@ -229,6 +238,7 @@ static ERL_NIF_TERM emdb_get_nif (ErlNifEnv * env,
{ {
ErlNifBinary key; ErlNifBinary key;
ErlNifBinary val; ErlNifBinary val;
/* ERL_NIF_TERM term; */
MDB_val mkey; MDB_val mkey;
MDB_val mdata; MDB_val mdata;
@ -265,7 +275,7 @@ static ERL_NIF_TERM emdb_get_nif (ErlNifEnv * env,
} }
if (! enif_alloc_binary(mdata.mv_size, & val)) if (! enif_alloc_binary(mdata.mv_size, & val))
FAIL_FAST(EMDB_MALLOC_ERR, err2); FAIL_FAST(EMDB_MALLOC_ERR, err2);
memcpy(val.data, mdata.mv_data, mdata.mv_size); memcpy(val.data, mdata.mv_data, mdata.mv_size);
@ -275,6 +285,16 @@ static ERL_NIF_TERM emdb_get_nif (ErlNifEnv * env,
atom_ok, atom_ok,
enif_make_binary(env, & val)); enif_make_binary(env, & val));
/* val.size = mdata.mv_size; */
/* val.data = mdata.mv_data; */
/* mdb_txn_abort(txn); */
/* return enif_make_tuple(env, 2, */
/* atom_ok, */
/* term); */
err2: err2:
mdb_txn_abort(txn); mdb_txn_abort(txn);
err1: err1:
@ -451,7 +471,7 @@ static void emdb_unload(ErlNifEnv* env, void* priv)
static ErlNifFunc nif_funcs [] = { static ErlNifFunc nif_funcs [] = {
{"open", 1, emdb_open_nif}, {"open", 3, emdb_open_nif},
{"close", 1, emdb_close_nif}, {"close", 1, emdb_close_nif},
{"put", 3, emdb_put_nif}, {"put", 3, emdb_put_nif},
{"get", 2, emdb_get_nif}, {"get", 2, emdb_get_nif},

36
include/emdb.hrl Normal file
View file

@ -0,0 +1,36 @@
%%-------------------------------------------------------------------
%% This file is part of EMDB - Erlang MDB API
%%
%% Copyright (c) 2012 by Aleph Archives. All rights reserved.
%%
%%-------------------------------------------------------------------
%% Redistribution and use in source and binary forms, with or without
%% modification, are permitted only as authorized by the OpenLDAP
%% Public License.
%%
%% A copy of this license is available in the file LICENSE in the
%% top-level directory of the distribution or, alternatively, at
%% <http://www.OpenLDAP.org/license.html>.
%%
%% Permission to use, copy, modify, and distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
%% copyright notice and this permission notice appear in all copies.
%%
%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
%% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
%% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
%% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
%% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
%% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
%% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
%%-------------------------------------------------------------------
%% Environment Flags
-define(MDB_FIXEDMAP, 16#01).
-define(MDB_NOSUBDIR, 16#02).
-define(MDB_NOSYNC, 16#10000).
-define(MDB_RDONLY, 16#20000).
-define(MDB_NOMETASYNC, 16#40000).
-define(MDB_WRITEMAP, 16#80000).
-define(MDB_MAPASYNC, 16#100000).

95
include/emdb.hrl~ Normal file
View file

@ -0,0 +1,95 @@
%% Copyright (c) 2011 Basho Technologies, Inc. All Rights Reserved.
%%
%% This file is provided to you under the Apache License,
%% Version 2.0 (the "License"); you may not use this file
%% except in compliance with the License. You may obtain
%% a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing,
%% software distributed under the License is distributed on an
%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
%% KIND, either express or implied. See the License for the
%% specific language governing permissions and limitations
%% under the License.
-define(LEVELS,
[debug, info, notice, warning, error, critical, alert, emergency, none]).
-define(DEBUG, 7).
-define(INFO, 6).
-define(NOTICE, 5).
-define(WARNING, 4).
-define(ERROR, 3).
-define(CRITICAL, 2).
-define(ALERT, 1).
-define(EMERGENCY, 0).
-define(LOG_NONE, -1).
-define(LEVEL2NUM(Level),
case Level of
debug -> ?DEBUG;
info -> ?INFO;
notice -> ?NOTICE;
warning -> ?WARNING;
error -> ?ERROR;
critical -> ?CRITICAL;
alert -> ?ALERT;
emergency -> ?EMERGENCY
end).
-define(NUM2LEVEL(Num),
case Num of
?DEBUG -> debug;
?INFO -> info;
?NOTICE -> notice;
?WARNING -> warning;
?ERROR -> error;
?CRITICAL -> critical;
?ALERT -> alert;
?EMERGENCY -> emergency
end).
-define(SHOULD_LOG(Level),
lager_util:level_to_num(Level) =< element(1, lager_mochiglobal:get(loglevel, {?LOG_NONE, []}))).
-define(NOTIFY(Level, Pid, Format, Args),
gen_event:notify(lager_event, {log, lager_util:level_to_num(Level),
lager_util:format_time(), [io_lib:format("[~p] ", [Level]),
io_lib:format("~p ", [Pid]), io_lib:format(Format, Args)]})).
%% FOR INTERNAL USE ONLY
%% internal non-blocking logging call
%% there's some special handing for when we try to log (usually errors) while
%% lager is still starting.
-ifdef(TEST).
-define(INT_LOG(Level, Format, Args),
case ?SHOULD_LOG(Level) of
true ->
?NOTIFY(Level, self(), Format, Args);
_ ->
ok
end).
-else.
-define(INT_LOG(Level, Format, Args),
Self = self(),
%% do this in a spawn so we don't cause a deadlock calling gen_event:which_handlers
%% from a gen_event handler
spawn(fun() ->
case catch(gen_event:which_handlers(lager_event)) of
X when X == []; X == {'EXIT', noproc} ->
%% there's no handlers yet or lager isn't running, try again
%% in half a second.
timer:sleep(500),
?NOTIFY(Level, Self, Format, Args);
_ ->
case ?SHOULD_LOG(Level) of
true ->
?NOTIFY(Level, Self, Format, Args);
_ ->
ok
end
end
end)).
-endif.

View file

@ -32,19 +32,23 @@
%% EXPORTS %% EXPORTS
%%==================================================================== %%====================================================================
-export([ -export([
open/1 open/1,
open/2,
open/3
]). ]).
%%==================================================================== %%====================================================================
%% Types %% Includes
%%==================================================================== %%====================================================================
-record(emdb_oop, { -include("emdb.hrl").
handle :: non_neg_integer()
}).
%%====================================================================
%% Macros
%%====================================================================
-define(MDB_MAP_SIZE, 10485760). %% 10MB
%%==================================================================== %%====================================================================
%% PUBLIC API %% PUBLIC API
%%==================================================================== %%====================================================================
@ -53,12 +57,14 @@
%% @doc Create a new MDB database %% @doc Create a new MDB database
%% @end %% @end
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
-spec open(file:name()) -> #emdb_oop{}.
open(DirName) -> open(DirName) ->
open(DirName, ?MDB_MAP_SIZE).
open(DirName, MapSize) when is_integer(MapSize) andalso MapSize > 0 ->
open(DirName, MapSize, 0).
open(DirName, MapSize, EnvFlags) when is_integer(MapSize) andalso MapSize > 0 andalso is_integer(EnvFlags) andalso EnvFlags >= 0 ->
%% ensure directory exists %% ensure directory exists
ok = filelib:ensure_dir(DirName ++ "/"), ok = filelib:ensure_dir(DirName ++ "/"),
decorate(emdb_drv:open(DirName)). decorate(emdb_drv:open(DirName, MapSize, EnvFlags)).
%%==================================================================== %%====================================================================
%% PRIVATE API %% PRIVATE API

View file

@ -31,7 +31,7 @@
%% EXPORTS %% EXPORTS
%%==================================================================== %%====================================================================
-export([ -export([
open/1, open/3,
close/1, close/1,
put/3, put/3,
@ -62,7 +62,7 @@
%% @doc %% @doc
%% @end %% @end
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------
open(_DirName) -> open(_DirName, _MapSize, _EnvFlags) ->
?NOT_LOADED. ?NOT_LOADED.
%%-------------------------------------------------------------------- %%--------------------------------------------------------------------