First draft of bookie
Now with the most simple of tests
This commit is contained in:
parent
e73a5bbf31
commit
5127119669
1 changed files with 70 additions and 23 deletions
|
@ -135,18 +135,20 @@
|
||||||
terminate/2,
|
terminate/2,
|
||||||
code_change/3,
|
code_change/3,
|
||||||
book_start/1,
|
book_start/1,
|
||||||
book_put/4,
|
book_riakput/3,
|
||||||
book_get/2,
|
book_riakget/3,
|
||||||
book_head/2,
|
book_riakhead/3,
|
||||||
|
book_close/1,
|
||||||
strip_to_keyonly/1,
|
strip_to_keyonly/1,
|
||||||
strip_to_keyseqonly/1,
|
strip_to_keyseqonly/1,
|
||||||
strip_to_seqonly/1,
|
strip_to_seqonly/1,
|
||||||
strip_to_statusonly/1,
|
strip_to_statusonly/1]).
|
||||||
strip_to_details/1]).
|
|
||||||
|
|
||||||
-include_lib("eunit/include/eunit.hrl").
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
|
||||||
-define(CACHE_SIZE, 1000).
|
-define(CACHE_SIZE, 1000).
|
||||||
|
-define(JOURNAL_FP, "journal").
|
||||||
|
-define(LEDGER_FP, "ledger").
|
||||||
|
|
||||||
-record(state, {inker :: pid(),
|
-record(state, {inker :: pid(),
|
||||||
penciller :: pid(),
|
penciller :: pid(),
|
||||||
|
@ -165,15 +167,21 @@
|
||||||
book_start(Opts) ->
|
book_start(Opts) ->
|
||||||
gen_server:start(?MODULE, [Opts], []).
|
gen_server:start(?MODULE, [Opts], []).
|
||||||
|
|
||||||
book_put(Pid, PrimaryKey, Object, IndexSpecs) ->
|
book_riakput(Pid, Object, IndexSpecs) ->
|
||||||
|
PrimaryKey = {o, Object#r_object.bucket, Object#r_object.key},
|
||||||
gen_server:call(Pid, {put, PrimaryKey, Object, IndexSpecs}, infinity).
|
gen_server:call(Pid, {put, PrimaryKey, Object, IndexSpecs}, infinity).
|
||||||
|
|
||||||
book_get(Pid, PrimaryKey) ->
|
book_riakget(Pid, Bucket, Key) ->
|
||||||
|
PrimaryKey = {o, Bucket, Key},
|
||||||
gen_server:call(Pid, {get, PrimaryKey}, infinity).
|
gen_server:call(Pid, {get, PrimaryKey}, infinity).
|
||||||
|
|
||||||
book_head(Pid, PrimaryKey) ->
|
book_riakhead(Pid, Bucket, Key) ->
|
||||||
|
PrimaryKey = {o, Bucket, Key},
|
||||||
gen_server:call(Pid, {head, PrimaryKey}, infinity).
|
gen_server:call(Pid, {head, PrimaryKey}, infinity).
|
||||||
|
|
||||||
|
book_close(Pid) ->
|
||||||
|
gen_server:call(Pid, close, infinity).
|
||||||
|
|
||||||
%%%============================================================================
|
%%%============================================================================
|
||||||
%%% gen_server callbacks
|
%%% gen_server callbacks
|
||||||
%%%============================================================================
|
%%%============================================================================
|
||||||
|
@ -208,7 +216,10 @@ init([Opts]) ->
|
||||||
|
|
||||||
|
|
||||||
handle_call({put, PrimaryKey, Object, IndexSpecs}, From, State) ->
|
handle_call({put, PrimaryKey, Object, IndexSpecs}, From, State) ->
|
||||||
{ok, SQN, ObjSize} = leveled_inker:ink_put(PrimaryKey, Object, IndexSpecs),
|
{ok, SQN, ObjSize} = leveled_inker:ink_put(State#state.inker,
|
||||||
|
PrimaryKey,
|
||||||
|
Object,
|
||||||
|
IndexSpecs),
|
||||||
Changes = preparefor_ledgercache(PrimaryKey,
|
Changes = preparefor_ledgercache(PrimaryKey,
|
||||||
SQN,
|
SQN,
|
||||||
Object,
|
Object,
|
||||||
|
@ -229,7 +240,7 @@ handle_call({get, Key}, _From, State) ->
|
||||||
not_present ->
|
not_present ->
|
||||||
{reply, not_found, State};
|
{reply, not_found, State};
|
||||||
Head ->
|
Head ->
|
||||||
{Key, Seqn, Status} = strip_to_details(Head),
|
{Seqn, Status, _MD} = striphead_to_details(Head),
|
||||||
case Status of
|
case Status of
|
||||||
{tomb, _} ->
|
{tomb, _} ->
|
||||||
{reply, not_found, State};
|
{reply, not_found, State};
|
||||||
|
@ -247,15 +258,17 @@ handle_call({head, Key}, _From, State) ->
|
||||||
not_present ->
|
not_present ->
|
||||||
{reply, not_found, State};
|
{reply, not_found, State};
|
||||||
Head ->
|
Head ->
|
||||||
{Key, _Seqn, Status} = strip_to_details(Head),
|
{_Seqn, Status, MD} = striphead_to_details(Head),
|
||||||
case Status of
|
case Status of
|
||||||
{tomb, _} ->
|
{tomb, _} ->
|
||||||
{reply, not_found, State};
|
{reply, not_found, State};
|
||||||
{active, _} ->
|
{active, _} ->
|
||||||
MD = strip_to_mdonly(Head),
|
OMD = build_metadata_object(Key, MD),
|
||||||
{reply, {ok, MD}, State}
|
{reply, {ok, OMD}, State}
|
||||||
end
|
end
|
||||||
end.
|
end;
|
||||||
|
handle_call(close, _From, State) ->
|
||||||
|
{stop, normal, ok, State}.
|
||||||
|
|
||||||
handle_cast(_Msg, State) ->
|
handle_cast(_Msg, State) ->
|
||||||
{noreply, State}.
|
{noreply, State}.
|
||||||
|
@ -263,8 +276,10 @@ handle_cast(_Msg, State) ->
|
||||||
handle_info(_Info, State) ->
|
handle_info(_Info, State) ->
|
||||||
{noreply, State}.
|
{noreply, State}.
|
||||||
|
|
||||||
terminate(_Reason, _State) ->
|
terminate(Reason, State) ->
|
||||||
ok.
|
io:format("Bookie closing for reason ~w~n", [Reason]),
|
||||||
|
ok = leveled_inker:ink_close(State#state.inker),
|
||||||
|
ok = leveled_penciller:pcl_close(State#state.penciller).
|
||||||
|
|
||||||
code_change(_OldVsn, State, _Extra) ->
|
code_change(_OldVsn, State, _Extra) ->
|
||||||
{ok, State}.
|
{ok, State}.
|
||||||
|
@ -275,14 +290,18 @@ code_change(_OldVsn, State, _Extra) ->
|
||||||
%%%============================================================================
|
%%%============================================================================
|
||||||
|
|
||||||
set_options(Opts) ->
|
set_options(Opts) ->
|
||||||
{#inker_options{root_path=Opts#bookie_options.root_path},
|
%% TODO: Change the max size default, and allow setting through options
|
||||||
#penciller_options{root_path=Opts#bookie_options.root_path}}.
|
{#inker_options{root_path = Opts#bookie_options.root_path ++
|
||||||
|
"/" ++ ?JOURNAL_FP,
|
||||||
|
cdb_options = #cdb_options{max_size=30000}},
|
||||||
|
#penciller_options{root_path=Opts#bookie_options.root_path ++
|
||||||
|
"/" ++ ?LEDGER_FP}}.
|
||||||
|
|
||||||
startup(InkerOpts, PencillerOpts) ->
|
startup(InkerOpts, PencillerOpts) ->
|
||||||
{ok, Inker} = leveled_inker:ink_start(InkerOpts),
|
{ok, Inker} = leveled_inker:ink_start(InkerOpts),
|
||||||
{ok, Penciller} = leveled_penciller:pcl_start(PencillerOpts),
|
{ok, Penciller} = leveled_penciller:pcl_start(PencillerOpts),
|
||||||
LedgerSQN = leveled_penciller:pcl_getstartupsequencenumber(Penciller),
|
LedgerSQN = leveled_penciller:pcl_getstartupsequencenumber(Penciller),
|
||||||
ok = leveled_inker:ink_loadpcl(LedgerSQN, fun load_fun/4, Penciller),
|
ok = leveled_inker:ink_loadpcl(Inker, LedgerSQN, fun load_fun/4, Penciller),
|
||||||
{Inker, Penciller}.
|
{Inker, Penciller}.
|
||||||
|
|
||||||
|
|
||||||
|
@ -319,9 +338,7 @@ strip_to_statusonly({_, {_, St, _}}) -> St.
|
||||||
|
|
||||||
strip_to_seqonly({_, {SeqN, _, _}}) -> SeqN.
|
strip_to_seqonly({_, {SeqN, _, _}}) -> SeqN.
|
||||||
|
|
||||||
strip_to_details({K, {SeqN, St, _}}) -> {K, SeqN, St}.
|
striphead_to_details({SeqN, St, MD}) -> {SeqN, St, MD}.
|
||||||
|
|
||||||
strip_to_mdonly({_, {_, _, MD}}) -> MD.
|
|
||||||
|
|
||||||
get_metadatas(#r_object{contents=Contents}) ->
|
get_metadatas(#r_object{contents=Contents}) ->
|
||||||
[Content#r_content.metadata || Content <- Contents].
|
[Content#r_content.metadata || Content <- Contents].
|
||||||
|
@ -341,6 +358,14 @@ hash(Obj=#r_object{}) ->
|
||||||
extract_metadata(Obj, Size) ->
|
extract_metadata(Obj, Size) ->
|
||||||
{get_metadatas(Obj), vclock(Obj), hash(Obj), Size}.
|
{get_metadatas(Obj), vclock(Obj), hash(Obj), Size}.
|
||||||
|
|
||||||
|
build_metadata_object(PrimaryKey, Head) ->
|
||||||
|
{o, Bucket, Key} = PrimaryKey,
|
||||||
|
{MD, VC, _, _} = Head,
|
||||||
|
Contents = lists:foldl(fun(X, Acc) -> Acc ++ [#r_content{metadata=X}] end,
|
||||||
|
[],
|
||||||
|
MD),
|
||||||
|
#r_object{contents=Contents, bucket=Bucket, key=Key, vclock=VC}.
|
||||||
|
|
||||||
convert_indexspecs(IndexSpecs, SQN, PrimaryKey) ->
|
convert_indexspecs(IndexSpecs, SQN, PrimaryKey) ->
|
||||||
lists:map(fun({IndexOp, IndexField, IndexValue}) ->
|
lists:map(fun({IndexOp, IndexField, IndexValue}) ->
|
||||||
Status = case IndexOp of
|
Status = case IndexOp of
|
||||||
|
@ -367,7 +392,7 @@ preparefor_ledgercache(PK, SQN, Obj, Size, IndexSpecs) ->
|
||||||
[PrimaryChange] ++ SecChanges.
|
[PrimaryChange] ++ SecChanges.
|
||||||
|
|
||||||
addto_ledgercache(Changes, Cache) ->
|
addto_ledgercache(Changes, Cache) ->
|
||||||
lists:foldl(fun({{K, V}, Acc}) -> gb_trees:enter(K, V, Acc) end,
|
lists:foldl(fun({K, V}, Acc) -> gb_trees:enter(K, V, Acc) end,
|
||||||
Cache,
|
Cache,
|
||||||
Changes).
|
Changes).
|
||||||
|
|
||||||
|
@ -412,4 +437,26 @@ load_fun(KeyInLedger, ValueInLedger, _Position, Acc0) ->
|
||||||
|
|
||||||
-ifdef(TEST).
|
-ifdef(TEST).
|
||||||
|
|
||||||
|
reset_filestructure() ->
|
||||||
|
RootPath = "../test",
|
||||||
|
leveled_inker:clean_testdir(RootPath ++ "/" ++ ?JOURNAL_FP),
|
||||||
|
leveled_penciller:clean_testdir(RootPath ++ "/" ++ ?LEDGER_FP),
|
||||||
|
RootPath.
|
||||||
|
|
||||||
|
single_key_test() ->
|
||||||
|
RootPath = reset_filestructure(),
|
||||||
|
{ok, Bookie} = book_start(#bookie_options{root_path=RootPath}),
|
||||||
|
{B1, K1, V1, Spec1, MD} = {"Bucket1",
|
||||||
|
"Key1",
|
||||||
|
"Value1",
|
||||||
|
[],
|
||||||
|
{"MDK1", "MDV1"}},
|
||||||
|
Content = #r_content{metadata=MD, value=V1},
|
||||||
|
Object = #r_object{bucket=B1, key=K1, contents=[Content], vclock=[{'a',1}]},
|
||||||
|
ok = book_riakput(Bookie, Object, Spec1),
|
||||||
|
{ok, F1} = book_riakget(Bookie, B1, K1),
|
||||||
|
?assertMatch(F1, Object),
|
||||||
|
ok = book_close(Bookie),
|
||||||
|
reset_filestructure().
|
||||||
|
|
||||||
-endif.
|
-endif.
|
Loading…
Add table
Add a link
Reference in a new issue