[erlang-questions] Example use of Berkeley DB

Chris Newcombe <>
Wed Nov 15 01:13:40 CET 2006


EDTK v1.5 (available from http://www.snookles.com/erlang/) contains a
complete driver for Berkeley DB.

The test-suite shows several different ways to use the driver
(different layers of APIs).  But it might help to have an example of
the recommended style of use.  So here it is.

This example does not use replication -- I will post another example
later for that.

BTW, I have sent a second EDTK v1.5 release candidate to Scott, so
that should be available on his server soon.  Unless someone finds a
problem, that second candidate will become the release.  But the
previous candidate is fine for experimentation.

regards,

Chris


After building EDTK and the berkeley_db driver, drop the following two
files into  edtk-1.5/examples/berkeley_db  and run the first one.


# File: examples_no_replication.sh

erlc -v -W +debug_info examples_no_replication.erl
mkdir -p example_bdb_data_dir

# Note that berkeley_db_drv.so must be in the Erlang *code* path
# (that happens to already be the case with this example)

LD_LIBRARY_PATH=../../../BerkeleyDB.4.5/lib \
erl -s examples_no_replication example_1



# File: examples_no_replication.erl

-module(examples_no_replication).
-export([example_1/0]).

%% It is possible to use the berkeley_db driver in several different
%% ways (the regression tests give examples of each).
%%
%% This file shows the recommended way.


%% Note that we don't need to include any .hrl files
-define(BDB_PC,berkeley_db_port_coordinator).
-define(BDB_H, berkeley_db_helpers).
-define(BDB,   berkeley_db_drv).


%% Internal functions exported for use with ?BDB_H:do_txn
-export([txn_fun_simple_put/2, txn_fun_simple_get/2,
         txn_fun_put_account/2, txn_fun_get_account_by_email/2]).


example_1() ->

    %% The port-coordiantor can be owned by a normal OTP supervisor
    %% (not shown here).
    %%
    %% This call creates a process that is locally registered as
    %% 'berkeley_db_port_coordinator'.  (You can also run multiple
    %% named port-coordinators, but most applications don't need to do
    %% that.)
    %%
    %% The only required argument is a BDB 'environment' directory,
    %% which must already exist.  (There are a lot of optional
    %% configuration arguments, not shown here.)

    {ok, _ServerPid} = ?BDB_PC:start_link("example_bdb_data_dir"),


    %% Now some example transactions
    %%
    %% Applications should call ?BDB_H:do_txn/2, which executes a
    %% {Module, Function, ExtraArgs} and will retry that operationq
    %% some number of times if BDB throws a deadlock exception. (See
    %% BDB documentation for an explanation of deadlocks and how to
    %% minimize them).

    %% Do a simple write (to a single key in a single database table)
    %% (note that key and data arguments may be strings or binaries)

    ok = ?BDB_H:do_txn({?MODULE, txn_fun_simple_put,
                        {"my-key", "my-data"}}, 3),

    %% Read it back (note that retrieved data is always returned as
    %% binaries)

    <<"my-data">> = ?BDB_H:do_txn({?MODULE, txn_fun_simple_get,
                                   "my-key"}, 3),


    %% A slightly more complex example: a multi-part transaction
    %% involving several databases

    ExampleAccountObj = [{email_addr, "my-email-address"},
                         {balance, 100}],

    ok = ?BDB_H:do_txn({?MODULE, txn_fun_put_account,
                        {"my-account-name", ExampleAccountObj}}, 3),

    {<<"my-account-name">>, AccountObjBin}
        = ?BDB_H:do_txn({?MODULE, txn_fun_get_account_by_email,
                         "my-email-address"}, 3),

    ExampleAccountObj = binary_to_term(AccountObjBin),


    %% Clean shutdown
    %%
    %% If owned by a supervisor, the port-coordinator should have a
    %% 'Shutdown policy' that is an integer (maybe 5000) - to allow it
    %% to cleanly close BDB.  Note that as BDB uses transactions and
    %% write-ahead logging, any committed data is safe even if the
    %% port-coordinator crashes or is killed.

    ok = ?BDB_PC:stop(),

    io:format("Done.~n"),
    erlang:halt().


txn_fun_simple_put(BdbPort, {Key, Data}) ->
    %% ... this creates the database on first use
    DB = ?BDB_PC:get_db_handle(BdbPort, bdb_DB_BTREE, "test.db"),

    %% Passing 'void' as the transaction handle means 'auto-commit')
    ok = ?BDB:db_put(BdbPort, DB, void, Key, Data, []).

txn_fun_simple_get(BdbPort, Key) ->
    DB = ?BDB_PC:get_db_handle(BdbPort, bdb_DB_BTREE, "test.db"),
    ?BDB:db_get_data(BdbPort, DB, void, Key, []).


txn_fun_put_account(BdbPort, {AccountName, AccountObj}) ->
    {value, {email_addr, EmailAddr}} = lists:keysearch(email_addr, 1,
AccountObj),

    AccountDB    = ?BDB_PC:get_db_handle(BdbPort, bdb_DB_BTREE, "account.db"),
    IndexEmailDB = ?BDB_PC:get_db_handle(BdbPort, bdb_DB_BTREE,
"index_email_to_account.db"),

    Txn = ?BDB:txn_begin(BdbPort, void, []),
    ok = ?BDB:db_put(BdbPort, AccountDB, Txn, AccountName,
term_to_binary(AccountObj), []),
    ok = ?BDB:db_put(BdbPort, IndexEmailDB, Txn, EmailAddr, AccountName, []),
    ok = ?BDB:txn_commit(BdbPort, Txn, []).


txn_fun_get_account_by_email(BdbPort, EmailAddr) ->
    AccountDB     = ?BDB_PC:get_db_handle(BdbPort, bdb_DB_BTREE, "account.db"),
    IndexEmailDB = ?BDB_PC:get_db_handle(BdbPort, bdb_DB_BTREE,
"index_email_to_account.db"),

    Txn = ?BDB:txn_begin(BdbPort, void, []),
    AccountName = ?BDB:db_get_data(BdbPort, IndexEmailDB, Txn, EmailAddr, []),
    AccountObj  = ?BDB:db_get_data(BdbPort, AccountDB, Txn, AccountName, []),
    ok = ?BDB:txn_commit(BdbPort, Txn, []),
    {AccountName, AccountObj}.



More information about the erlang-questions mailing list