make wterl an app, cache the WT connection

Add app, supervisor, and worker to open and cache the database
connection. Revert to using binaries for WT config in order to allow
wterl callers to use proplists for configuration. Change {error,
not_found} to not_found return values from wterl.
This commit is contained in:
Steve Vinoski 2012-02-17 00:42:22 -05:00
parent c89d6deaea
commit 7e6d947765
6 changed files with 239 additions and 36 deletions

View file

@ -46,7 +46,6 @@ typedef struct
WT_CURSOR* cursor;
} wterl_cursor_handle;
typedef char Config[256]; // configuration strings
typedef char Uri[128]; // object names
// Atoms (initialized in on_load)
@ -115,7 +114,7 @@ static ErlNifFunc nif_funcs[] =
static inline ERL_NIF_TERM wterl_strerror(ErlNifEnv* env, int rc)
{
if (rc == WT_NOTFOUND)
return enif_make_tuple2(env, ATOM_ERROR, enif_make_atom(env, "not_found"));
return enif_make_atom(env, "not_found");
else
return enif_make_tuple2(env, ATOM_ERROR,
enif_make_string(env, wiredtiger_strerror(rc), ERL_NIF_LATIN1));
@ -123,13 +122,13 @@ static inline ERL_NIF_TERM wterl_strerror(ErlNifEnv* env, int rc)
static ERL_NIF_TERM wterl_conn_open(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
Config config;
ErlNifBinary config;
char homedir[4096];
if (enif_get_string(env, argv[0], homedir, sizeof homedir, ERL_NIF_LATIN1) &&
enif_get_string(env, argv[1], config, sizeof config, ERL_NIF_LATIN1))
enif_inspect_binary(env, argv[1], &config))
{
WT_CONNECTION* conn;
int rc = wiredtiger_open(homedir, NULL, config, &conn);
int rc = wiredtiger_open(homedir, NULL, (const char*)config.data, &conn);
if (rc == 0)
{
wterl_conn_handle* conn_handle =
@ -179,36 +178,36 @@ static inline ERL_NIF_TERM wterl_session_worker(ErlNifEnv* env, int argc, const
if (enif_get_resource(env, argv[0], wterl_session_RESOURCE, (void**)&session_handle))
{
WT_SESSION* session = session_handle->session;
Config config;
ErlNifBinary config;
Uri uri;
if (enif_get_string(env, argv[1], uri, sizeof uri, ERL_NIF_LATIN1) &&
enif_get_string(env, argv[2], config, sizeof config, ERL_NIF_LATIN1))
enif_inspect_binary(env, argv[2], &config))
{
int rc;
switch (op) {
case WTERL_OP_CREATE:
rc = session->create(session, uri, config);
rc = session->create(session, uri, (const char*)config.data);
break;
case WTERL_OP_DROP:
rc = session->drop(session, uri, config);
rc = session->drop(session, uri, (const char*)config.data);
break;
case WTERL_OP_SALVAGE:
rc = session->salvage(session, uri, config);
rc = session->salvage(session, uri, (const char*)config.data);
break;
case WTERL_OP_SYNC:
rc = session->sync(session, uri, config);
rc = session->sync(session, uri, (const char*)config.data);
break;
case WTERL_OP_TRUNCATE:
// Ignore the cursor start/stop form of truncation for now,
// support only the full file truncation.
rc = session->truncate(session, uri, NULL, NULL, config);
rc = session->truncate(session, uri, NULL, NULL, (const char*)config.data);
break;
case WTERL_OP_UPGRADE:
rc = session->upgrade(session, uri, config);
rc = session->upgrade(session, uri, (const char*)config.data);
break;
default:
case WTERL_OP_VERIFY:
rc = session->verify(session, uri, config);
rc = session->verify(session, uri, (const char*)config.data);
break;
}
if (rc != 0)
@ -285,13 +284,13 @@ static ERL_NIF_TERM wterl_session_rename(ErlNifEnv* env, int argc, const ERL_NIF
if (enif_get_resource(env, argv[0], wterl_session_RESOURCE, (void**)&session_handle))
{
WT_SESSION* session = session_handle->session;
Config config;
ErlNifBinary config;
Uri oldname, newname;
if (enif_get_string(env, argv[1], oldname, sizeof oldname, ERL_NIF_LATIN1) &&
enif_get_string(env, argv[2], newname, sizeof newname, ERL_NIF_LATIN1) &&
enif_get_string(env, argv[3], config, sizeof config, ERL_NIF_LATIN1))
enif_inspect_binary(env, argv[3], &config))
{
int rc = session->rename(session, oldname, newname, config);
int rc = session->rename(session, oldname, newname, (const char*)config.data);
if (rc != 0)
{
return wterl_strerror(env, rc);

View file

@ -7,6 +7,7 @@
kernel,
stdlib
]},
{mod, {wterl_app, []}},
{env, [
]}
]}.

View file

@ -55,7 +55,7 @@
session_upgrade/3,
session_verify/2,
session_verify/3,
config_to_bin/2]).
config_to_bin/1]).
-on_load(init/0).
@ -63,6 +63,8 @@
nif_stub_error(Line) ->
erlang:nif_error({nif_not_loaded,module,?MODULE,line,Line}).
-define(EMPTY_CONFIG, <<"\0">>).
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-endif.
@ -91,12 +93,12 @@ session_close(_Ref) ->
?nif_stub.
session_create(Ref, Name) ->
session_create(Ref, Name, "").
session_create(Ref, Name, ?EMPTY_CONFIG).
session_create(_Ref, _Name, _Config) ->
?nif_stub.
session_drop(Ref, Name) ->
session_drop(Ref, Name, "").
session_drop(Ref, Name, ?EMPTY_CONFIG).
session_drop(_Ref, _Name, _Config) ->
?nif_stub.
@ -110,32 +112,32 @@ session_put(_Ref, _Table, _Key, _Value) ->
?nif_stub.
session_rename(Ref, OldName, NewName) ->
session_rename(Ref, OldName, NewName, "").
session_rename(Ref, OldName, NewName, ?EMPTY_CONFIG).
session_rename(_Ref, _OldName, _NewName, _Config) ->
?nif_stub.
session_salvage(Ref, Name) ->
session_salvage(Ref, Name, "").
session_salvage(Ref, Name, ?EMPTY_CONFIG).
session_salvage(_Ref, _Name, _Config) ->
?nif_stub.
session_sync(Ref, Name) ->
session_sync(Ref, Name, "").
session_sync(Ref, Name, ?EMPTY_CONFIG).
session_sync(_Ref, _Name, _Config) ->
?nif_stub.
session_truncate(Ref, Name) ->
session_truncate(Ref, Name, "").
session_truncate(Ref, Name, ?EMPTY_CONFIG).
session_truncate(_Ref, _Name, _Config) ->
?nif_stub.
session_upgrade(Ref, Name) ->
session_upgrade(Ref, Name, "").
session_upgrade(Ref, Name, ?EMPTY_CONFIG).
session_upgrade(_Ref, _Name, _Config) ->
?nif_stub.
session_verify(Ref, Name) ->
session_verify(Ref, Name, "").
session_verify(Ref, Name, ?EMPTY_CONFIG).
session_verify(_Ref, _Name, _Config) ->
?nif_stub.
@ -179,6 +181,7 @@ config_types() ->
{eviction_target, integer},
{eviction_trigger, integer},
{extensions, string},
{force, bool},
{hazard_max, integer},
{home_environment, bool},
{home_environment_priv, bool},
@ -203,6 +206,8 @@ config_encode(bool, false) ->
config_encode(_Type, _Value) ->
invalid.
config_to_bin(Opts) ->
config_to_bin(Opts, []).
config_to_bin([], Acc) ->
iolist_to_binary([Acc, <<"\0">>]);
config_to_bin([{Key, Value} | Rest], Acc) ->
@ -215,7 +220,7 @@ config_to_bin([{Key, Value} | Rest], Acc) ->
Acc;
EncodedValue ->
EncodedKey = atom_to_binary(Key, utf8),
[EncodedKey, <<" = ">>, EncodedValue, <<", ">> | Acc]
[EncodedKey, <<"=">>, EncodedValue, <<",">> | Acc]
end,
config_to_bin(Rest, Acc2);
false ->
@ -235,13 +240,13 @@ basic_test() ->
ok = filelib:ensure_dir(filename:join("/tmp/wterl.basic", "foo")),
io:put_chars(standard_error, "\topen connection\n"),
{ok, ConnRef} =
conn_open("/tmp/wterl.basic", "create=true,cache_size=100MB"),
OpenConfig = config_to_bin([{create,true},{cache_size,"100MB"}]),
{ok, ConnRef} = conn_open("/tmp/wterl.basic", OpenConfig),
{ok, SRef} = session_open(ConnRef),
%% Remove the table from any earlier run and re-create it
io:put_chars(standard_error, "\ttable drop/create\n"),
ok = session_drop(SRef, "table:test", "force"),
ok = session_drop(SRef, "table:test", config_to_bin([{force,true}])),
ok = session_create(SRef, "table:test"),
%% Insert/delete a key using the session handle
@ -249,7 +254,7 @@ basic_test() ->
ok = session_put(SRef, "table:test", <<"a">>, <<"apple">>),
{ok, <<"apple">>} = session_get(SRef, "table:test", <<"a">>),
ok = session_delete(SRef, "table:test", <<"a">>),
{error, not_found} = session_get(SRef, "table:test", <<"a">>),
not_found = session_get(SRef, "table:test", <<"a">>),
%% Insert some items
io:put_chars(standard_error, "\tsession insert\n"),
@ -277,7 +282,7 @@ basic_test() ->
io:put_chars(standard_error, "\tsession truncate\n"),
ok = session_truncate(SRef, "table:test"),
{error, not_found} = session_get(SRef, "table:test", <<"a">>),
not_found = session_get(SRef, "table:test", <<"a">>),
ok = session_put(SRef, "table:test", <<"a">>, <<"apple">>),
ok = session_put(SRef, "table:test", <<"b">>, <<"banana">>),
@ -314,7 +319,7 @@ basic_test() ->
{ok, <<"cherry">>} = cursor_prev(Cursor),
{ok, <<"date">>} = cursor_next(Cursor),
{ok, <<"gooseberry">>} = cursor_next(Cursor),
{error, not_found} = cursor_next(Cursor),
not_found = cursor_next(Cursor),
ok = cursor_close(Cursor),
%% Search for an item
@ -352,7 +357,7 @@ basic_test() ->
io:put_chars(standard_error, "\tcursor update\n"),
{ok, Cursor} = cursor_open(SRef, "table:test"),
ok = cursor_update(Cursor, <<"g">>, <<"goji berries">>),
{error, not_found} = cursor_update(Cursor, <<"k">>, <<"kumquat">>),
not_found = cursor_update(Cursor, <<"k">>, <<"kumquat">>),
ok = cursor_close(Cursor),
{ok, <<"goji berries">>} = session_get(SRef, "table:test", <<"g">>),
@ -360,9 +365,9 @@ basic_test() ->
io:put_chars(standard_error, "\tcursor remove\n"),
{ok, Cursor} = cursor_open(SRef, "table:test"),
ok = cursor_remove(Cursor, <<"g">>, <<"goji berries">>),
{error, not_found} = cursor_remove(Cursor, <<"l">>, <<"lemon">>),
not_found = cursor_remove(Cursor, <<"l">>, <<"lemon">>),
ok = cursor_close(Cursor),
{error, not_found} = session_get(SRef, "table:test", <<"g">>),
not_found = session_get(SRef, "table:test", <<"g">>),
%% Close the session/connection
io:put_chars(standard_error, "\tsession/connection close\n"),

39
src/wterl_app.erl Normal file
View file

@ -0,0 +1,39 @@
%% -------------------------------------------------------------------
%%
%% wterl: access to WiredTiger database
%%
%% Copyright (c) 2012 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.
%%
%% -------------------------------------------------------------------
-module(wterl_app).
-author('Steve Vinoski <steve@basho.com>').
-behaviour(application).
%% Application callbacks
-export([start/2, stop/1]).
%% ===================================================================
%% Application callbacks
%% ===================================================================
start(_StartType, _StartArgs) ->
wterl_sup:start_link().
stop(_State) ->
ok.

111
src/wterl_conn.erl Normal file
View file

@ -0,0 +1,111 @@
%% -------------------------------------------------------------------
%%
%% wterl_conn: manage a connection to WiredTiger
%%
%% Copyright (c) 2012 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.
%%
%% -------------------------------------------------------------------
-module(wterl_conn).
-author('Steve Vinoski <steve@basho.com>').
-behaviour(gen_server).
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-endif.
%% API
-export([start_link/0,
open/1, get/0, close/0]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-record(state, { conn }).
%% ====================================================================
%% API
%% ====================================================================
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
open(Dir) ->
gen_server:call(?MODULE, {open, Dir}, infinity).
get() ->
gen_server:call(?MODULE, get, infinity).
close() ->
gen_server:call(?MODULE, close, infinity).
%% ====================================================================
%% gen_server callbacks
%% ====================================================================
init([]) ->
{ok, #state{}}.
handle_call({open, Dir}, _From, #state{conn = undefined}=State) ->
Opts = [{create, true}, {cache_size, "100MB"}],
{Reply, NState} = case wterl:conn_open(Dir, wterl:config_to_bin(Opts)) of
{ok, ConnRef}=OK ->
{OK, State#state{conn = ConnRef}};
Error ->
{Error, State}
end,
{reply, Reply, NState};
handle_call({open, _Dir}, _From,#state{conn = ConnRef}=State) ->
{reply, {ok, ConnRef}, State};
handle_call(get, _From, #state{conn = undefined}=State) ->
{reply, {error, "no connection"}, State};
handle_call(get, _From, #state{conn = ConnRef}=State) ->
{reply, {ok, ConnRef}, State};
handle_call(close, _From, #state{conn = ConnRef}=State) ->
close(ConnRef),
{reply, ok, State}.
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, #state{conn = ConnRef}) ->
close(ConnRef),
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%% ====================================================================
%% Internal functions
%% ====================================================================
close(undefined) ->
ok;
close(ConnRef) ->
wterl:conn_close(ConnRef).
-ifdef(TEST).
-endif.

48
src/wterl_sup.erl Normal file
View file

@ -0,0 +1,48 @@
%% -------------------------------------------------------------------
%%
%% wterl_sup: supervisor for WiredTiger database app
%%
%% Copyright (c) 2012 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.
%%
%% -------------------------------------------------------------------
-module(wterl_sup).
-author('Steve Vinoski <steve@basho.com>').
-behaviour(supervisor).
%% API
-export([start_link/0]).
%% Supervisor callbacks
-export([init/1]).
%% Helper macro for declaring children of supervisor
-define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}).
%% ===================================================================
%% API functions
%% ===================================================================
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
%% ===================================================================
%% Supervisor callbacks
%% ===================================================================
init([]) ->
{ok, {{one_for_one, 5, 10}, [?CHILD(wterl_conn, worker)]}}.