Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Refactor innostore backend module to use new backend API.

Fixes: az710

This change will bring the innostore backend into sync with the rest
of the backends with regard to the API including support for
asynchronous folds and the delete/4 function.

Additionally, the test suite has been replaced with a suite of tests
that are similar to those used by the other backends.
  • Loading branch information...
commit 931141595f68dd1f9065deb259594fd4b2cd71d2 1 parent 7041710
@kellymclaughlin kellymclaughlin authored
View
49 Makefile
@@ -1,25 +1,54 @@
-INNOSTORE_TAG = $(shell hg identify -t)
+INNOSTORE_TAG = $(shell git describe --tags)
-all: # Innostore only works with the SMP runtime, force it on uniprocessors
- ERL_FLAGS="-smp enable" ./rebar compile eunit verbose=1
+.PHONY: rel deps package pkgclean
-clean: distclean
+all: deps compile test
+
+compile:
+ ./rebar compile
+
+clean:
./rebar clean
+test:
+ ./rebar eunit
+
install:
./rebar install
+# Release tarball creation
+# Generates a tarball that includes all the deps sources so no checkouts are necessary
+archivegit = git archive --format=tar --prefix=$(1)/ HEAD | (cd $(2) && tar xf -)
+archivehg = hg archive $(2)/$(1)
+archive = if [ -d ".git" ]; then \
+ $(call archivegit,$(1),$(2)); \
+ else \
+ $(call archivehg,$(1),$(2)); \
+ fi
+
+buildtar = mkdir distdir && \
+ git clone . distdir/$(REPO)-clone && \
+ cd distdir/$(REPO)-clone && \
+ git checkout $(INNOSTORE_TAG) && \
+ $(call archive,$(INNOSTORE_TAG),..) && \
+ mkdir ../$(INNOSTORE_TAG)/deps && \
+ make deps; \
+ for dep in deps/*; do cd $${dep} && $(call archive,$${dep},../../../$(INNOSTORE_TAG)); cd ../..; done
+
distdir:
- $(if $(findstring tip,$(INNOSTORE_TAG)),$(error "You can't generate a release tarball from tip"))
- mkdir distdir
- hg clone -u $(INNOSTORE_TAG) . distdir/innostore-clone
- cd distdir/innostore-clone; \
- hg archive ../$(INNOSTORE_TAG)
+ $(if $(INNOSTORE_TAG), $(call buildtar), $(error "You can't generate a release tarball from a non-tagged revision. Run 'git checkout <tag>', then 'make dist'"))
dist $(INNOSTORE_TAG).tar.gz: distdir
cd distdir; \
tar czf ../$(INNOSTORE_TAG).tar.gz $(INNOSTORE_TAG)
-distclean:
+ballclean:
rm -rf $(INNOSTORE_TAG).tar.gz distdir
+package: dist
+ $(MAKE) -C package package
+
+pkgclean:
+ $(MAKE) -C package pkgclean
+
+export INNOSTORE_TAG PKG_VERSION REPO REVISION
View
BIN  rebar
Binary file not shown
View
9 rebar.config
@@ -1,3 +1,7 @@
+{so_name, "innostore_drv.so"}.
+{erl_opts, [debug_info, warnings_as_errors]}.
+{deps, []}.
+
{port_envs, [
{"DRV_CFLAGS", "$DRV_CFLAGS -Werror -I c_src/innodb/include"},
{"DRV_LDFLAGS", "$DRV_LDFLAGS c_src/innodb/lib/libinnodb.a"},
@@ -15,9 +19,8 @@
{"darwin10.*-32$", "LDFLAGS", "-arch i386"}
]}.
-{port_pre_script, {"c_src/build_deps.sh",
- "c_src/innodb/lib/libinnodb.a"}}.
+{pre_hooks, [{compile, "c_src/build_deps.sh"}]}.
-{port_cleanup_script, "c_src/build_deps.sh clean"}.
+{post_hooks, [{clean, "c_src/build_deps.sh clean"}]}.
{cover_enabled, true}.
View
762 src/riak_kv_innostore_backend.erl
@@ -24,33 +24,54 @@
-author('Dave Smith <dizzyd@basho.com>').
-%% Public API for riak
--export([start/2,
+%% KV Backend API
+-export([api_version/0,
+ start/2,
stop/1,
- get/2,
- put/3,
- delete/2,
- list/1,
- fold_bucket_keys/3,
- list_bucket/2,
- fold/3,
- is_empty/1,
+ get/3,
+ put/5,
+ delete/4,
drop/1,
- callback/3,
- status/0, status/1]).
-
+ fold_buckets/4,
+ fold_keys/4,
+ fold_objects/4,
+ is_empty/1,
+ status/1,
+ callback/3]).
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-endif.
+-define(API_VERSION, 1).
+-define(CAPABILITIES, [async_fold]).
+
-record(state, { partition_str,
port }).
+-opaque(state() :: #state{}).
+-type config() :: [{atom(), term()}].
+-type bucket() :: binary().
+-type key() :: binary().
+-type fold_buckets_fun() :: fun((binary(), any()) -> any() | no_return()).
+-type fold_keys_fun() :: fun((binary(), binary(), any()) -> any() |
+ no_return()).
+-type fold_objects_fun() :: fun((binary(), binary(), term(), any()) ->
+ any() |
+ no_return()).
+
%% ===================================================================
%% Public API
%% ===================================================================
+%% @doc Return the major version of the
+%% current API and a capabilities list.
+-spec api_version() -> {integer(), [atom()]}.
+api_version() ->
+ {?API_VERSION, ?CAPABILITIES}.
+
+%% @doc Start the innostore backend
+-spec start(integer(), config()) -> {ok, state()} | {error, term()}.
start(Partition, _Config) ->
case innostore:connect() of
{ok, Port} ->
@@ -60,165 +81,343 @@ start(Partition, _Config) ->
{error, Reason}
end.
+%% @doc Stop the innostore backend
+-spec stop(state()) -> ok.
stop(State) ->
innostore:disconnect(State#state.port).
-get(State, {Bucket, Key}) ->
- case innostore:get(Key, keystore(Bucket, State)) of
+%% @doc Retrieve an object from the innostore backend
+-spec get(bucket(), key(), state()) ->
+ {ok, any(), state()} |
+ {ok, not_found, state()} |
+ {error, term(), state()}.
+get(Bucket, Key, #state{partition_str=Partition,
+ port=Port}=State) ->
+ case innostore:get(Key, keystore(Bucket, Partition, Port)) of
{ok, not_found} ->
- {error, notfound};
+ {error, not_found, State};
{ok, Value} ->
- {ok, Value};
+ {ok, Value, State};
{error, Reason} ->
- {error, Reason}
+ {error, Reason, State}
end.
-put(State, {Bucket, Key}, Value) ->
- innostore:put(Key, Value, keystore(Bucket, State)).
-
-delete(State, {Bucket, Key}) ->
- innostore:delete(Key, keystore(Bucket, State)).
-
-list(State) ->
- %% List all keys in all buckets
- list_keys(true, list_buckets(State), [], undefined, State).
-
-list_bucket(State, '_') -> %% List bucket names
- [bucket_from_tablename(TN) || TN <- list_buckets(State)];
-list_bucket(State, {filter, Bucket, Fun}) ->
- %% Filter keys in a bucket
- Name = <<Bucket/binary, (State#state.partition_str)/binary>>,
- list_keys(false, [Name], [], Fun, State);
-list_bucket(State, Bucket) ->
- %% List all keys in a bucket
- Name = <<Bucket/binary, (State#state.partition_str)/binary>>,
- list_keys(false, [Name], [], undefined, State).
-
-fold_bucket_keys(State, Bucket0, Visitor) when is_function(Visitor) ->
- Bucket = <<Bucket0/binary, (State#state.partition_str)/binary>>,
- {ok, Store} = innostore:open_keystore(Bucket, State#state.port),
- case innostore:fold_keys(Visitor, [], Store) of
+%% @doc Insert an object into the innostore backend.
+%% NOTE: The innostore backend does not currently support
+%% secondary indexing and the _IndexSpecs parameter
+%% is ignored.
+-type index_spec() :: {add, Index, SecondaryKey} | {remove, Index, SecondaryKey}.
+-spec put(riak_object:bucket(), riak_object:key(), [index_spec()], binary(), state()) ->
+ {ok, state()} |
+ {error, term(), state()}.
+put(Bucket, Key, _IndexSpecs, Value, #state{partition_str=Partition,
+ port=Port}=State) ->
+ KeyStore = keystore(Bucket, Partition, Port),
+ case innostore:put(Key, Value, KeyStore) of
+ ok ->
+ {ok, State};
{error, Reason} ->
- {error, Reason};
- Acc ->
- Acc
+ {error, Reason, State}
end.
-fold(State, Fun0, Acc0) ->
- fold_buckets(list_buckets(State), State, Fun0, Acc0).
+%% @doc Delete an object from the innostore backend
+%% NOTE: The innostore backend does not currently support
+%% secondary indexing and the _IndexSpecs parameter
+%% is ignored.
+-spec delete(bucket(), key(), [index_spec()], state()) ->
+ {ok, state()} |
+ {error, term(), state()}.
+delete(Bucket, Key, _IndexSpecs, #state{partition_str=Partition,
+ port=Port}=State) ->
+ KeyStore = keystore(Bucket, Partition, Port),
+ case innostore:delete(Key, KeyStore) of
+ ok ->
+ {ok, State};
+ {error, Reason} ->
+ {error, Reason, State}
+ end.
-is_empty(State) ->
- lists:all(fun(I) -> I end,
- [innostore:is_keystore_empty(B, State#state.port) ||
- B <- list_buckets(State)]).
+%% @doc Fold over all the buckets.
+-spec fold_buckets(fold_buckets_fun(),
+ any(),
+ [],
+ state()) -> {ok, any()} | {async, fun()} | {error, term()}.
+fold_buckets(FoldBucketsFun, Acc, Opts, #state{partition_str=Partition,
+ port=Port}) ->
+ FoldFun = fold_buckets_fun(FoldBucketsFun),
+ Suffix = binary_to_list(Partition),
+ FilterFun =
+ fun(KeyStore, Acc1) ->
+ case lists:suffix(Suffix, KeyStore) of
+ true ->
+ Bucket = bucket_from_tablename(KeyStore),
+ FoldFun(Bucket, Acc1);
+ false ->
+ Acc1
+ end
+ end,
+ case lists:member(async_fold, Opts) of
+ true ->
+ BucketFolder =
+ fun() ->
+ case innostore:connect() of
+ {ok, Port1} ->
+ FoldResults =
+ lists:foldl(FilterFun,
+ Acc,
+ innostore:list_keystores(Port1)),
+ innostore:disconnect(Port1),
+ FoldResults;
+ {error, Reason} ->
+ {error, Reason}
+ end
+ end,
+ {async, BucketFolder};
+ false ->
+ FoldResults = lists:foldl(FilterFun,
+ Acc,
+ innostore:list_keystores(Port)),
+ {ok, FoldResults}
+ end.
-drop(State) ->
- KSes = list_buckets(State),
- [innostore:drop_keystore(K, State#state.port) || K <- KSes],
- ok.
+%% @doc Fold over all the keys for one or all buckets.
+-spec fold_keys(fold_keys_fun(),
+ any(),
+ [{atom(), term()}],
+ state()) -> {ok, term()} | {error, term()}.
+fold_keys(FoldKeysFun, Acc, Opts, #state{partition_str=Partition,
+ port=Port}) ->
+ Bucket = proplists:get_value(bucket, Opts),
+ case lists:member(async_fold, Opts) of
+ true ->
+ KeyFolder = async_key_folder(Bucket, FoldKeysFun, Acc, Partition),
+ {async, KeyFolder};
+ false ->
+ FoldResults = sync_key_fold(Bucket, FoldKeysFun, Acc, Partition, Port),
+ {ok, FoldResults}
+ end.
-status() ->
- status([]).
+%% @doc Fold over all the objects for one or all buckets.
+-spec fold_objects(fold_objects_fun(),
+ any(),
+ [{atom(), term()}],
+ state()) -> {ok, any()} | {error, term()}.
+fold_objects(FoldObjectsFun, Acc, Opts, #state{partition_str=Partition,
+ port=Port}) ->
+ Bucket = proplists:get_value(bucket, Opts),
+ case lists:member(async_fold, Opts) of
+ true ->
+ KeyFolder = async_object_folder(Bucket, FoldObjectsFun, Acc, Partition),
+ {async, KeyFolder};
+ false ->
+ FoldResults = sync_object_fold(Bucket, FoldObjectsFun, Acc, Partition, Port),
+ {ok, FoldResults}
+ end.
-status(Names) ->
- {ok, Port} = innostore:connect(),
- try
- Status = case Names of
- [] ->
- innostore:status(Port);
- _ ->
- [begin
- A = to_atom(N),
- {A, innostore:status(A, Port)}
- end || N <- Names]
- end,
- format_status(Status)
- after
- innostore:disconnect(Port)
- end,
- ok.
+%% @doc Delete all objects from this innostore backend
+-spec drop(state()) -> {ok, state()} | {error, term(), state()}.
+drop(#state{partition_str=Partition,
+ port=Port}=State) ->
+ KeyStores = list_keystores(Partition, Port),
+ [innostore:drop_keystore(KeyStore, Port) || KeyStore <- KeyStores],
+ {ok, State}.
+
+%% @doc Returns true if this innostore backend contains any
+%% non-tombstone values; otherwise returns false.
+-spec is_empty(state()) -> boolean() | {error, term()}.
+is_empty(#state{partition_str=Partition,
+ port=Port}) ->
+ lists:all(fun(I) -> I end,
+ [innostore:is_keystore_empty(B, Port) ||
+ B <- list_keystores(Partition, Port)]).
-%% Ignore callbacks we do not know about - may be in multi backend
-callback(_State, _Ref, _Msg) ->
- ok.
+%% @doc Get the status information for this innostore backend
+-spec status(state()) -> [{atom(), term()}].
+status(#state{port=Port}) ->
+ Status = innostore:status(Port),
+ format_status(Status).
+%% Ignore callbacks we do not know about - may be in multi backend
+callback(_Ref, _Msg, State) ->
+ {ok, State}.
%% ===================================================================
%% Internal functions
%% ===================================================================
-key_entry(undefined,Key) -> Key;
-key_entry(Bucket,Key) -> {Bucket,Key}.
-
-keystore(Bucket, State) ->
- Name = <<Bucket/binary, (State#state.partition_str)/binary>>,
- case erlang:get({innostore, Name}) of
+%% @private
+keystore(Bucket, Partition, Port) ->
+ KeyStoreId = <<Bucket/binary, Partition/binary>>,
+ case erlang:get({innostore, KeyStoreId}) of
undefined ->
- {ok, Store} = innostore:open_keystore(Name, State#state.port),
- erlang:put({innostore, Name}, Store),
- Store;
- Store ->
- Store
+ {ok, KeyStore} = innostore:open_keystore(KeyStoreId, Port),
+ erlang:put({innostore, KeyStore}, KeyStore),
+ KeyStore;
+ KeyStore ->
+ KeyStore
+ end.
+
+%% @private
+%% Return a function to fold over the buckets on this backend
+fold_buckets_fun(FoldBucketsFun) ->
+ fun(Bucket, Acc) ->
+ FoldBucketsFun(Bucket, Acc)
+ end.
+
+%% @private
+%% Return a function to synchronously fold over keys on this backend
+async_key_folder(undefined, FoldFun, Acc, Partition) ->
+ fun() ->
+ case innostore:connect() of
+ {ok, Port} ->
+ Buckets = list_buckets(Partition, Port),
+ %% Fold over all keys in all buckets
+ FoldResults = fold_all_keys(Buckets,
+ Acc,
+ FoldFun,
+ Partition,
+ Port),
+ innostore:disconnect(Port),
+ FoldResults;
+ {error, Reason} ->
+ {error, Reason}
+ end
+ end;
+async_key_folder(Bucket, FoldFun, Acc, Partition) ->
+ FoldKeysFun = fold_keys_fun(FoldFun, Bucket),
+ fun() ->
+ case innostore:connect() of
+ {ok, Port} ->
+ KeyStore = keystore(Bucket, Partition, Port),
+ FoldResults =
+ innostore:fold_keys(FoldKeysFun, Acc, KeyStore),
+ innostore:disconnect(Port),
+ FoldResults;
+ {error, Reason} ->
+ {error, Reason}
+ end
+ end.
+
+%% @private
+%% Return a function to synchronously fold over keys on this backend
+sync_key_fold(undefined, FoldFun, Acc, Partition, Port) ->
+ Buckets = list_buckets(Partition, Port),
+ %% Fold over all keys in all buckets
+ fold_all_keys(Buckets,
+ Acc,
+ FoldFun,
+ Partition,
+ Port);
+sync_key_fold(Bucket, FoldFun, Acc, Partition, Port) ->
+ FoldKeysFun = fold_keys_fun(FoldFun, Bucket),
+ KeyStore = keystore(Bucket, Partition, Port),
+ innostore:fold_keys(FoldKeysFun, Acc, KeyStore).
+
+%% @private
+fold_keys_fun(FoldKeysFun, Bucket) ->
+ fun(Key, Acc) ->
+ FoldKeysFun(Bucket, Key, Acc)
+ end.
+
+%% @private
+%% Return a function to synchronously fold over objects on this backend
+sync_object_fold(undefined, FoldFun, Acc, Partition, Port) ->
+ Buckets = list_buckets(Partition, Port),
+ %% Fold over all keys in all buckets
+ fold_all_objects(Buckets,
+ Acc,
+ FoldFun,
+ Partition,
+ Port);
+sync_object_fold(Bucket, FoldFun, Acc, Partition, Port) ->
+ FoldObjectsFun = fold_objects_fun(FoldFun, Bucket),
+ KeyStore = keystore(Bucket, Partition, Port),
+ innostore:fold_keys(FoldObjectsFun, Acc, KeyStore).
+
+%% @private
+%% Return a function to synchronously fold over objects on this backend
+async_object_folder(undefined, FoldFun, Acc, Partition) ->
+ fun() ->
+ case innostore:connect() of
+ {ok, Port} ->
+ Buckets = list_buckets(Partition, Port),
+ %% Fold over all objects in all buckets
+ FoldResults = fold_all_objects(Buckets,
+ Acc,
+ FoldFun,
+ Partition,
+ Port),
+ innostore:disconnect(Port),
+ FoldResults;
+ {error, Reason} ->
+ {error, Reason}
+ end
+ end;
+async_object_folder(Bucket, FoldFun, Acc, Partition) ->
+ FoldObjectsFun = fold_objects_fun(FoldFun, Bucket),
+ fun() ->
+ case innostore:connect() of
+ {ok, Port} ->
+ KeyStore = keystore(Bucket, Partition, Port),
+ FoldResults = innostore:fold(FoldObjectsFun, Acc, KeyStore),
+ innostore:disconnect(Port),
+ FoldResults;
+ {error, Reason} ->
+ {error, Reason}
+ end
+ end.
+
+%% @private
+%% Return a function to fold over keys on this backend
+fold_objects_fun(FoldObjectsFun, Bucket) ->
+ fun(Key, Value, Acc) ->
+ FoldObjectsFun(Bucket, Key, Value, Acc)
end.
-list_buckets(State) ->
- Suffix = binary_to_list(State#state.partition_str),
- [T || T <- innostore:list_keystores(State#state.port),
- lists:suffix(Suffix, T) == true].
+%% @private
+list_buckets(Partition, Port) ->
+ Suffix = binary_to_list(Partition),
+ [bucket_from_tablename(KeyStore) || KeyStore <- innostore:list_keystores(Port),
+ lists:suffix(Suffix, KeyStore) == true].
-list_keys(_IncludeBucket, [], Acc, _Pred, _State) ->
+%% @private
+list_keystores(Partition, Port) ->
+ Suffix = binary_to_list(Partition),
+ [KeyStore || KeyStore <- innostore:list_keystores(Port),
+ lists:suffix(Suffix, KeyStore) == true].
+
+%% @private
+fold_all_keys([], Acc, _, _Partition, _Port) ->
Acc;
-list_keys(IncludeBucket, [Name | Rest], Acc, Pred, State) ->
- Bucket = case IncludeBucket of
- true -> bucket_from_tablename(Name);
- false -> undefined
- end,
- case Pred of
- undefined ->
- Visitor = fun(K, Acc1) -> [key_entry(Bucket, K) | Acc1] end;
-
- Pred when is_function(Pred) ->
- Visitor = fun(K, Acc1) ->
- Entry = key_entry(Bucket, K),
- case Pred(Entry) of
- true ->
- [Entry | Acc1];
- false ->
- Acc1
- end
- end
- end,
- {ok, Store} = innostore:open_keystore(Name, State#state.port),
- case innostore:fold_keys(Visitor, Acc, Store) of
+fold_all_keys([Bucket | RestBuckets], Acc, FoldKeysFun, Partition, Port) ->
+ KeyStore = keystore(Bucket, Partition, Port),
+ FoldFun = fold_keys_fun(FoldKeysFun, Bucket),
+ case innostore:fold_keys(FoldFun, Acc, KeyStore) of
{error, Reason} ->
{error, Reason};
- Acc2 ->
- list_keys(IncludeBucket, Rest, Acc2, Pred, State)
+ Acc1 ->
+ fold_all_keys(RestBuckets, Acc1, FoldKeysFun, Partition, Port)
end.
-fold_buckets([], _State, _Fun, Acc0) ->
- Acc0;
-fold_buckets([B | Rest], State, Fun, Acc0) ->
- Bucket = bucket_from_tablename(B),
- F = fun(K, V, A) ->
- Fun({Bucket, K}, V, A)
- end,
- Acc = innostore:fold(F, Acc0, keystore(Bucket, State)),
- fold_buckets(Rest, State, Fun, Acc).
-
-
+%% @private
+fold_all_objects([], Acc, _, _Partition, _Port) ->
+ Acc;
+fold_all_objects([Bucket | RestBuckets], Acc, FoldObjectsFun, Partition, Port) ->
+ KeyStore = keystore(Bucket, Partition, Port),
+ FoldFun = fold_objects_fun(FoldObjectsFun, Bucket),
+ case innostore:fold(FoldFun, Acc, KeyStore) of
+ {error, Reason} ->
+ {error, Reason};
+ Acc1 ->
+ fold_all_objects(RestBuckets, Acc1, FoldObjectsFun, Partition, Port)
+ end.
+%% @private
bucket_from_tablename(TableName) ->
{match, [Name]} = re:run(TableName, "(.*)_\\d+", [{capture, all_but_first, binary}]),
Name.
-to_atom(A) when is_atom(A) ->
- A;
-to_atom(S) when is_list(S) ->
- list_to_existing_atom(S);
-to_atom(B) when is_binary(B) ->
- binary_to_existing_atom(B, utf8).
-
+%% @private
format_status([]) -> ok;
format_status([{K,V}|T]) ->
io:format("~p: ~p~n", [K,V]),
@@ -229,96 +428,211 @@ format_status([{K,V}|T]) ->
%% ===================================================================
-ifdef(TEST).
--define(TEST_BUCKET, <<"test">>).
--define(OTHER_TEST_BUCKET, <<"othertest">>).
-
-innostore_riak_test_() ->
- {spawn, [{"bucket_list",
- ?_test(
- begin
- reset(),
- {ok, S1} = start(0, undefined),
- {ok, S2} = start(1, undefined),
-
- ok = ?MODULE:put(S1, {?TEST_BUCKET, <<"p0key1">>}, <<"abcdef">>),
- ok = ?MODULE:put(S1, {?TEST_BUCKET, <<"p0key2">>}, <<"abcdef">>),
- ok = ?MODULE:put(S2, {?TEST_BUCKET, <<"p1key2">>}, <<"dasdf">>),
- ok = ?MODULE:put(S1, {?OTHER_TEST_BUCKET, <<"p0key3">>}, <<"123456">>),
-
- ["othertest_0", "test_0"] = lists:sort(list_buckets(S1)),
- ["test_1"] = list_buckets(S2),
-
- ?assertEqual([?OTHER_TEST_BUCKET, ?TEST_BUCKET],
- lists:sort(list_bucket(S1, '_'))),
-
- ?assertEqual([<<"p0key1">>,<<"p0key2">>],
- lists:sort(list_bucket(S1, ?TEST_BUCKET))),
-
- ?assertEqual([<<"p0key3">>],
- lists:sort(list_bucket(S1, ?OTHER_TEST_BUCKET))),
-
- FindKey1 = fun(<<"p0key1">>) -> true; (_) -> false end,
- ?assertEqual([<<"p0key1">>],
- lists:sort(list_bucket(S1, {filter, ?TEST_BUCKET, FindKey1}))),
-
- NotKey1 = fun(<<"p0key1">>) -> false; (_) -> true end,
- ?assertEqual([<<"p0key2">>],
- lists:sort(list_bucket(S1, {filter, ?TEST_BUCKET, NotKey1}))),
-
-
- ?assertEqual([{?OTHER_TEST_BUCKET, <<"p0key3">>},
- {?TEST_BUCKET, <<"p0key1">>},
- {?TEST_BUCKET, <<"p0key2">>}],
- lists:sort(?MODULE:list(S1))),
- ?assertEqual([{?TEST_BUCKET, <<"p1key2">>}],
- ?MODULE:list(S2))
- end)},
-
- {"fold_bucket_keys_test",
- ?_test(
- begin
- reset(),
- {ok, S1} = start(5, undefined),
- ok = ?MODULE:put(S1, {?TEST_BUCKET, <<"abc">>}, <<"123">>),
- ok = ?MODULE:put(S1, {?TEST_BUCKET, <<"def">>}, <<"456">>),
- ok = ?MODULE:put(S1, {?TEST_BUCKET, <<"ghi">>}, <<"789">>),
- F = fun(Key, Accum) -> [{?TEST_BUCKET, Key}|Accum] end,
- [{?TEST_BUCKET, <<"ghi">>},
- {?TEST_BUCKET, <<"def">>},
- {?TEST_BUCKET, <<"abc">>}] =
- ?MODULE:fold_bucket_keys(S1, ?TEST_BUCKET, F)
- end)},
-
- {"fold_test",
- ?_test(
- begin
- reset(),
- {ok, S} = start(2, undefined),
- ok = ?MODULE:put(S, {?TEST_BUCKET, <<"1">>}, <<"abcdef">>),
- ok = ?MODULE:put(S, {?TEST_BUCKET, <<"2">>}, <<"foo">>),
- ok = ?MODULE:put(S, {?TEST_BUCKET, <<"3">>}, <<"bar">>),
- ok = ?MODULE:put(S, {?TEST_BUCKET, <<"4">>}, <<"baz">>),
- [{{?TEST_BUCKET, <<"4">>}, <<"baz">>},
- {{?TEST_BUCKET, <<"3">>}, <<"bar">>},
- {{?TEST_BUCKET, <<"2">>}, <<"foo">>},
- {{?TEST_BUCKET, <<"1">>}, <<"abcdef">>}]
- = ?MODULE:fold(S, fun(K,V,A)->[{K,V}|A] end, [])
- end)},
- {"status test",
- ?_test(
- begin
- ?assertEqual(ok, status()),
- ?assertEqual(ok, status([page_size])),
- ?assertEqual(ok, status(["page_size"])),
- ?assertEqual(ok, status([<<"page_size">>]))
- end)}
- ]}.
-
-reset() ->
+standard_test_() ->
+ Config = [
+ {data_home_dir, "./test/innodb-backend"},
+ {log_group_home_dir, "./test/innodb-backend"},
+ {buffer_pool_size, 2147483648}
+ ],
+ {spawn,
+ [
+ {setup,
+ fun() -> setup(Config) end,
+ fun cleanup/1,
+ fun(X) ->
+ [basic_store_and_fetch(X),
+ fold_buckets(X),
+ fold_keys(X),
+ delete_object(X),
+ fold_objects(X),
+ empty_check(X)
+ ]
+ end
+ }]}.
+
+basic_store_and_fetch(State) ->
+ {"basic store and fetch test",
+ fun() ->
+ [
+ ?_assertMatch({ok, _},
+ ?MODULE:put(<<"b1">>, <<"k1">>, [], <<"v1">>, State)),
+ ?_assertMatch({ok, _},
+ ?MODULE:put(<<"b2">>, <<"k2">>, [], <<"v2">>, State)),
+ ?_assertMatch({ok,<<"v2">>, _},
+ ?MODULE:get(<<"b2">>, <<"k2">>, State)),
+ ?_assertMatch({error, not_found, _},
+ ?MODULE:get(<<"b1">>, <<"k3">>, State))
+ ]
+ end
+ }.
+
+fold_buckets(State) ->
+ {"bucket folding test",
+ fun() ->
+ FoldBucketsFun =
+ fun(Bucket, Acc) ->
+ [Bucket | Acc]
+ end,
+
+ ?_assertEqual([<<"b1">>, <<"b2">>],
+ begin
+ {ok, Buckets1} =
+ ?MODULE:fold_buckets(FoldBucketsFun,
+ [],
+ [],
+ State),
+ lists:sort(Buckets1)
+ end)
+ end
+ }.
+
+fold_keys(State) ->
+ {"key folding test",
+ fun() ->
+ FoldKeysFun =
+ fun(Bucket, Key, Acc) ->
+ [{Bucket, Key} | Acc]
+ end,
+ FoldKeysFun1 =
+ fun(_Bucket, Key, Acc) ->
+ [Key | Acc]
+ end,
+ FoldKeysFun2 =
+ fun(Bucket, Key, Acc) ->
+ case Bucket =:= <<"b1">> of
+ true ->
+ [Key | Acc];
+ false ->
+ Acc
+ end
+ end,
+ FoldKeysFun3 =
+ fun(Bucket, Key, Acc) ->
+ case Bucket =:= <<"b1">> of
+ true ->
+ Acc;
+ false ->
+ [Key | Acc]
+ end
+ end,
+ [
+ ?_assertEqual([{<<"b1">>, <<"k1">>}, {<<"b2">>, <<"k2">>}],
+ begin
+ {ok, Keys1} =
+ ?MODULE:fold_keys(FoldKeysFun,
+ [],
+ [],
+ State),
+ lists:sort(Keys1)
+ end),
+ ?_assertEqual({ok, [<<"k1">>]},
+ ?MODULE:fold_keys(FoldKeysFun1,
+ [],
+ [{bucket, <<"b1">>}],
+ State)),
+ ?_assertEqual([<<"k2">>],
+ ?MODULE:fold_keys(FoldKeysFun1,
+ [],
+ [{bucket, <<"b2">>}],
+ State)),
+ ?_assertEqual({ok, [<<"k1">>]},
+ ?MODULE:fold_keys(FoldKeysFun2, [], [], State)),
+ ?_assertEqual({ok, [<<"k1">>]},
+ ?MODULE:fold_keys(FoldKeysFun2,
+ [],
+ [{bucket, <<"b1">>}],
+ State)),
+ ?_assertEqual({ok, [<<"k2">>]},
+ ?MODULE:fold_keys(FoldKeysFun3, [], [], State)),
+ ?_assertEqual({ok, []},
+ ?MODULE:fold_keys(FoldKeysFun3,
+ [],
+ [{bucket, <<"b1">>}],
+ State))
+ ]
+ end
+ }.
+
+delete_object(State) ->
+ {"object deletion test",
+ fun() ->
+ [
+ ?_assertMatch({ok, _}, ?MODULE:delete(<<"b2">>, <<"k2">>, State)),
+ ?_assertMatch({error, not_found, _},
+ ?MODULE:get(<<"b2">>, <<"k2">>, State))
+ ]
+ end
+ }.
+
+fold_objects(State) ->
+ {"object folding test",
+ fun() ->
+ FoldKeysFun =
+ fun(Bucket, Key, Acc) ->
+ [{Bucket, Key} | Acc]
+ end,
+ FoldObjectsFun =
+ fun(Bucket, Key, Value, Acc) ->
+ [{{Bucket, Key}, Value} | Acc]
+ end,
+ [
+ ?_assertEqual([{<<"b1">>, <<"k1">>}],
+ begin
+ {ok, Keys} =
+ ?MODULE:fold_keys(FoldKeysFun,
+ [],
+ [],
+ State),
+ lists:sort(Keys)
+ end),
+
+ ?_assertEqual([{{<<"b1">>,<<"k1">>}, <<"v1">>}],
+ begin
+ {ok, Objects1} =
+ ?MODULE:fold_objects(FoldObjectsFun,
+ [],
+ [],
+ State),
+ lists:sort(Objects1)
+ end),
+ ?_assertMatch({ok, _},
+ ?MODULE:put(<<"b3">>, <<"k3">>, [], <<"v3">>, State)),
+ ?_assertEqual([{{<<"b1">>,<<"k1">>},<<"v1">>},
+ {{<<"b3">>,<<"k3">>},<<"v3">>}],
+ begin
+ {ok, Objects} =
+ ?MODULE:fold_objects(FoldObjectsFun,
+ [],
+ [],
+ State),
+ lists:sort(Objects)
+ end)
+ ]
+ end
+ }.
+
+empty_check(State) ->
+ {"is_empty test",
+ fun() ->
+ [
+ ?_assertEqual(false, ?MODULE:is_empty(State)),
+ ?_assertMatch({ok, _}, ?MODULE:delete(<<"b1">>,<<"k1">>, State)),
+ ?_assertMatch({ok, _}, ?MODULE:delete(<<"b3">>,<<"k3">>, State)),
+ ?_assertEqual(true, ?MODULE:is_empty(State))
+ ]
+ end
+ }.
+
+setup(Config) ->
+ %% Start the backend
+ {ok, S} = ?MODULE:start(42, Config),
+ S.
+
+cleanup(S) ->
+ ok = ?MODULE:stop(S),
{ok, Port} = innostore:connect(),
[ok = innostore:drop_keystore(T, Port) || T <- innostore:list_keystores(Port)],
+ innostore:disconnect(Port),
ok.
-
-
-endif.
Please sign in to comment.
Something went wrong with that request. Please try again.