%%%------------------------------------------------------------------- %%% @doc %%% This is the main module, which contains all Shards/ETS API %%% functions, BUT works locally. %%% %%% Shards is compatible with ETS API, most of the functions %%% preserves the same ETS semantics, with some exception which you %%% will find on each function doc. %%% %%% Shards gives a top level view of a single logical ETS table, %%% but inside, that logical table is split in multiple physical %%% ETS tables called shards, where `Shards = [0 .. N-1]', %%% and `N' is the number of shards into which you want to split %%% the table. %%% %%% The K/V pairs are distributed across these shards, therefore, %%% some of the functions does not follows the same semantics as %%% the original ones ETS. %%% %%% A good example of that are the query-based functions, which %%% returns multiple results, and in case of `ordered_set', with %%% a particular order. E.g.: %%%
WARNING: It is not recommended execute %% this function, since it might cause an unexpected behavior. %% Once this function is executed, `shards' doesn't control/manage %% the ETS shards anymore. So from this point, you should use %% ETS API instead. Also it is recommended to run `shards:delete/1' %% after run this function. %%
%% %% @see ets:give_away/3. %% @end -spec give_away(Tab, Pid, GiftData, State) -> true when Tab :: atom(), Pid :: pid(), GiftData :: term(), State :: shards_state:state(). give_away(Tab, Pid, GiftData, State) -> Map = {fun shards_owner:give_away/3, [Pid, GiftData]}, Reduce = {fun(_, Acc) -> Acc end, true}, mapred(Tab, Map, Reduce, State). %% @equiv ets:i() i() -> ets:i(). %% @equiv info(Tab, shards_state:new()) info(Tab) -> info(Tab, shards_state:new()). %% @doc %% If 2nd argument is `info_tuple()' this function behaves like %% `ets:info/2', but if it is the `shards_state:state()', %% it behaves like `ets:info/1', but instead of return the %% information about one single table, it returns a list with %% the information of each shard table. %% %% @see ets:info/1. %% @see ets:info/2. %% @see shards:info_shard/2. %% @see shards:info_shard/3. %% @end -spec info(Tab, StateOrItem) -> Result when Tab :: atom(), StateOrItem :: shards_state:state() | info_item(), InfoList :: [info_tuple()], Result1 :: [InfoList] | undefined, Value :: [term()] | undefined, Result :: Result1 | Value. info(Tab, Item) when is_atom(Item) -> info(Tab, Item, shards_state:new()); info(Tab, State) -> case whereis(Tab) of undefined -> undefined; _ -> mapred(Tab, fun ets:info/1, State) end. %% @doc %% This operation behaves like `ets:info/2', but instead of return %% the information about one single table, it returns a list with %% the information of each shard table. %% %% @see ets:info/2. %% @see shards:info_shard/3. %% @end -spec info(Tab, Item, State) -> Value when Tab :: atom(), State :: shards_state:state(), Item :: info_item(), Value :: [term()] | undefined. info(Tab, Item, State) -> case whereis(Tab) of undefined -> undefined; _ -> mapred(Tab, {fun ets:info/2, [Item]}, State) end. %% @doc %% This operation behaves like `ets:info/1' %% %% @see ets:info/1. %% @end -spec info_shard(Tab, Shard) -> InfoList | undefined when Tab :: atom(), Shard :: non_neg_integer(), InfoList :: [info_tuple()]. info_shard(Tab, Shard) -> ShardName = shard_name(Tab, Shard), ets:info(ShardName). %% @doc %% This operation behaves like `ets:info/2'. %% %% @see ets:info/2. %% @end -spec info_shard(Tab, Shard, Item) -> Value | undefined when Tab :: atom(), Shard :: non_neg_integer(), Item :: info_item(), Value :: term(). info_shard(Tab, Shard, Item) -> ShardName = shard_name(Tab, Shard), ets:info(ShardName, Item). %% @equiv insert(Tab, ObjOrObjL, shards_state:new()) insert(Tab, ObjOrObjL) -> insert(Tab, ObjOrObjL, shards_state:new()). %% @doc %% This operation behaves similar to `ets:insert/2', with a big %% difference, it is not atomic. This means if it fails %% inserting some K/V pair, previous inserted KV pairs are not %% rolled back. %% %% @see ets:insert/2. %% @end -spec insert(Tab, ObjOrObjL, State) -> true when Tab :: atom(), ObjOrObjL :: tuple() | [tuple()], State :: shards_state:state(). insert(Tab, ObjOrObjL, State) when is_list(ObjOrObjL) -> lists:foreach(fun(Object) -> true = insert(Tab, Object, State) end, ObjOrObjL), true; insert(Tab, ObjOrObjL, State) when is_tuple(ObjOrObjL) -> [Key | _] = tuple_to_list(ObjOrObjL), N = shards_state:n_shards(State), PickShardFun = shards_state:pick_shard_fun(State), ShardName = shard_name(Tab, PickShardFun(Key, N, w)), ets:insert(ShardName, ObjOrObjL). %% @equiv insert_new(Tab, ObjOrObjL, shards_state:new()) insert_new(Tab, ObjOrObjL) -> insert_new(Tab, ObjOrObjL, shards_state:new()). %% @doc %% This operation behaves like `ets:insert_new/2' BUT it is not atomic, %% which means if it fails inserting some K/V pair, only that K/V %% pair is affected, the rest may be successfully inserted. %% %% This function returns a list if the `ObjectOrObjects' is a list. %% %% @see ets:insert_new/2. %% @end -spec insert_new(Tab, ObjOrObjL, State) -> Result when Tab :: atom(), ObjOrObjL :: tuple() | [tuple()], State :: shards_state:state(), Result :: boolean() | [boolean()]. insert_new(Tab, ObjOrObjL, State) when is_list(ObjOrObjL) -> lists:foldr(fun(Object, Acc) -> [insert_new(Tab, Object, State) | Acc] end, [], ObjOrObjL); insert_new(Tab, ObjOrObjL, State) when is_tuple(ObjOrObjL) -> [Key | _] = tuple_to_list(ObjOrObjL), N = shards_state:n_shards(State), PickShardFun = shards_state:pick_shard_fun(State), case PickShardFun(Key, N, r) of any -> Map = {fun ets:lookup/2, [Key]}, Reduce = fun lists:append/2, case mapred(Tab, Map, Reduce, State) of [] -> ShardName = shard_name(Tab, PickShardFun(Key, N, w)), ets:insert_new(ShardName, ObjOrObjL); _ -> false end; _ -> ShardName = shard_name(Tab, PickShardFun(Key, N, w)), ets:insert_new(ShardName, ObjOrObjL) end. %% @equiv ets:is_compiled_ms(Term) is_compiled_ms(Term) -> ets:is_compiled_ms(Term). %% @equiv last(Tab, shards_state:new()) last(Tab) -> last(Tab, shards_state:new()). %% @doc %% This operation behaves similar to `ets:last/1'. %% The order in which results are returned, might be not the same %% as the original ETS function. Remember shards architecture %% described at the beginning. %% %% @see ets:last/1. %% @end -spec last(Tab, State) -> Key | '$end_of_table' when Tab :: atom(), State :: shards_state:state(), Key :: term(). last(Tab, State) -> case ets:info(shard_name(Tab, 0), type) of ordered_set -> ets:last(shard_name(Tab, 0)); _ -> first(Tab, State) end. %% @equiv lookup(Tab, Key, shards_state:new()) lookup(Tab, Key) -> lookup(Tab, Key, shards_state:new()). %% @doc %% This operation behaves like `ets:lookup/2'. %% %% @see ets:lookup/2. %% @end -spec lookup(Tab, Key, State) -> Result when Tab :: atom(), Key :: term(), State :: shards_state:state(), Result :: [tuple()]. lookup(Tab, Key, State) -> Map = {fun ets:lookup/2, [Key]}, Reduce = fun lists:append/2, mapred(Tab, Key, Map, Reduce, State, r). %% @equiv lookup_element(Tab, Key, Pos, shards_state:new()) lookup_element(Tab, Key, Pos) -> lookup_element(Tab, Key, Pos, shards_state:new()). %% @doc %% This operation behaves like `ets:lookup_element/3'. %% %% @see ets:lookup_element/3. %% @end -spec lookup_element(Tab, Key, Pos, State) -> Elem when Tab :: atom(), Key :: term(), Pos :: pos_integer(), State :: shards_state:state(), Elem :: term() | [term()]. lookup_element(Tab, Key, Pos, State) -> N = shards_state:n_shards(State), PickShardFun = shards_state:pick_shard_fun(State), case PickShardFun(Key, N, r) of any -> LookupElem = fun(Tx, Kx, Px) -> catch ets:lookup_element(Tx, Kx, Px) end, Filter = lists:filter(fun ({'EXIT', _}) -> false; (_) -> true end, mapred(Tab, {LookupElem, [Key, Pos]}, State)), case Filter of [] -> exit({badarg, erlang:get_stacktrace()}); _ -> lists:append(Filter) end; Shard -> ShardName = shard_name(Tab, Shard), ets:lookup_element(ShardName, Key, Pos) end. match(Tab, Pattern) -> match(Tab, Pattern, shards_state:new()). %% @doc %% If 3rd argument is `pos_integer()' this function behaves %% like `ets:match/3', but if it is the `shards_state:state()', %% it behaves like `ets:match/2'. %% %% The order in which results are returned, might be not the same %% as the original ETS function. Remember shards architecture %% described at the beginning. %% %% @see ets:match/2. %% @see ets:match/3. %% @end -spec match(Tab, Pattern, StateOrLimit) -> Response when Tab :: atom(), Pattern :: ets:match_pattern(), StateOrLimit :: shards_state:state() | pos_integer(), Match :: [term()], Continuation :: continuation(), ResWithState :: [Match], ResWithLimit :: {[Match], Continuation} | '$end_of_table', Response :: ResWithState | ResWithLimit. match(Tab, Pattern, Limit) when is_integer(Limit), Limit > 0 -> match(Tab, Pattern, Limit, shards_state:new()); match(Tab, Pattern, State) -> Map = {fun ets:match/2, [Pattern]}, Reduce = fun lists:append/2, mapred(Tab, Map, Reduce, State). %% @doc %% This operation behaves similar to `ets:match/3'. %% The order in which results are returned, might be not the same %% as the original ETS function. Remember shards architecture %% described at the beginning. %% %% @see ets:match/3. %% @end -spec match(Tab, Pattern, Limit, State) -> Response when Tab :: atom(), Pattern :: ets:match_pattern(), Limit :: pos_integer(), State :: shards_state:state(), Match :: term(), Continuation :: continuation(), Response :: {[Match], Continuation} | '$end_of_table'. match(Tab, Pattern, Limit, State) -> N = shards_state:n_shards(State), q(match, Tab, Pattern, Limit, q_fun(), Limit, N - 1, {[], nil}). %% @doc %% This operation behaves similar to `ets:match/1'. %% The order in which results are returned, might be not the same %% as the original ETS function. Remember shards architecture %% described at the beginning. %% %% @see ets:match/1. %% @end -spec match(Continuation) -> Response when Match :: term(), Continuation :: continuation(), Response :: {[Match], Continuation} | '$end_of_table'. match({_, _, Limit, _, _} = Continuation) -> q(match, Continuation, q_fun(), Limit, []). %% @equiv match_delete(Tab, Pattern, shards_state:new()) match_delete(Tab, Pattern) -> match_delete(Tab, Pattern, shards_state:new()). %% @doc %% This operation behaves like `ets:match_delete/2'. %% %% @see ets:match_delete/2. %% @end -spec match_delete(Tab, Pattern, State) -> true when Tab :: atom(), Pattern :: ets:match_pattern(), State :: shards_state:state(). match_delete(Tab, Pattern, State) -> Map = {fun ets:match_delete/2, [Pattern]}, Reduce = {fun(Res, Acc) -> Acc and Res end, true}, mapred(Tab, Map, Reduce, State). %% @equiv match_object(Tab, Pattern, shards_state:new()) match_object(Tab, Pattern) -> match_object(Tab, Pattern, shards_state:new()). %% @doc %% If 3rd argument is `pos_integer()' this function behaves like %% `ets:match_object/3', but if it is the `shards_state:state()', %% it behaves like `ets:match_object/2'. %% %% The order in which results are returned, might be not the same %% as the original ETS function. Remember shards architecture %% described at the beginning. %% %% @see ets:match_object/2. %% @see ets:match_object/3. %% @end -spec match_object(Tab, Pattern, StateOrLimit) -> Response when Tab :: atom(), Pattern :: ets:match_pattern(), StateOrLimit :: shards_state:state() | pos_integer(), Object :: tuple(), ResWithState :: [Object], ResWithLimit :: {[term()], Continuation} | '$end_of_table', Continuation :: continuation(), Response :: ResWithState | ResWithLimit. match_object(Tab, Pattern, Limit) when is_integer(Limit), Limit > 0 -> match_object(Tab, Pattern, Limit, shards_state:new()); match_object(Tab, Pattern, State) -> Map = {fun ets:match_object/2, [Pattern]}, Reduce = fun lists:append/2, mapred(Tab, Map, Reduce, State). %% @doc %% This operation behaves similar to `ets:match_object/3'. %% The order in which results are returned, might be not the same %% as the original ETS function. Remember shards architecture %% described at the beginning. %% %% @see ets:match_object/3. %% @end -spec match_object(Tab, Pattern, Limit, State) -> Response when Tab :: atom(), Pattern :: ets:match_pattern(), Limit :: pos_integer(), State :: shards_state:state(), Match :: term(), Continuation :: continuation(), Response :: {[Match], Continuation} | '$end_of_table'. match_object(Tab, Pattern, Limit, State) -> N = shards_state:n_shards(State), q(match_object, Tab, Pattern, Limit, q_fun(), Limit, N - 1, {[], nil}). %% @doc %% This operation behaves similar to `ets:match_object/1'. %% The order in which results are returned, might be not the same %% as the original ETS function. Remember shards architecture %% described at the beginning. %% %% @see ets:match_object/1. %% @end -spec match_object(Continuation) -> Response when Match :: term(), Continuation :: continuation(), Response :: {[Match], Continuation} | '$end_of_table'. match_object({_, _, Limit, _, _} = Continuation) -> q(match_object, Continuation, q_fun(), Limit, []). %% @equiv ets:match_spec_compile(MatchSpec) match_spec_compile(MatchSpec) -> ets:match_spec_compile(MatchSpec). %% @equiv ets:match_spec_run(List, CompiledMatchSpec) match_spec_run(List, CompiledMatchSpec) -> ets:match_spec_run(List, CompiledMatchSpec). %% @equiv member(Tab, Key, shards_state:new()) member(Tab, Key) -> member(Tab, Key, shards_state:new()). %% @doc %% This operation behaves like `ets:member/2'. %% %% @see ets:member/2. %% @end -spec member(Tab, Key, State) -> boolean() when Tab :: atom(), Key :: term(), State :: shards_state:state(). member(Tab, Key, State) -> case mapred(Tab, Key, {fun ets:member/2, [Key]}, nil, State, r) of R when is_list(R) -> lists:member(true, R); R -> R end. %% @doc %% This operation is analogous to `ets:new/2', BUT it behaves totally %% different. When this function is called, instead of create a single %% table, a new supervision tree is created and added to `shards_sup'. %% %% This supervision tree has a main supervisor `shards_sup' which %% creates a control ETS table and also creates `N' number of %% `shards_owner' (being `N' the number of shards). Each `shards_owner' %% creates an ETS table to represent each shard, so this `gen_server' %% acts as the table owner. %% %% Finally, when you create a table, internally `N' physical tables %% are created (one per shard), but `shards' encapsulates all this %% and you see only one logical table (similar to how a distributed %% storage works). %% %% IMPORTANT: By default, `NumShards = number of schedulers'. %% %% @see ets:new/2. %% @end -spec new(Name, Options) -> Result when Name :: atom(), Options :: [option()], State :: shards_state:state(), Result :: {Name, State}. new(Name, Options) -> case shards_sup:start_child([Name, Options]) of {ok, _} -> {Name, shards_state:get(Name)}; _ -> throw(badarg) end. %% @equiv next(Tab, Key1, shards_state:new()) next(Tab, Key1) -> next(Tab, Key1, shards_state:new()). %% @doc %% This operation behaves similar to `ets:next/2'. %% The order in which results are returned, might be not the same %% as the original ETS function. Remember shards architecture %% described at the beginning. It raises a `bad_pick_fun_ret' %% exception in case of pick fun returns `any'. %% %% @see ets:next/2. %% @end -spec next(Tab, Key1, State) -> Key2 | '$end_of_table' when Tab :: atom(), Key1 :: term(), State :: shards_state:state(), Key2 :: term(). next(Tab, Key1, State) -> N = shards_state:n_shards(State), PickShardFun = shards_state:pick_shard_fun(State), case PickShardFun(Key1, N, r) of any -> throw(bad_pick_fun_ret); Shard -> ShardName = shard_name(Tab, Shard), next_(Tab, ets:next(ShardName, Key1), Shard) end. %% @private next_(Tab, '$end_of_table', Shard) when Shard > 0 -> NextShard = Shard - 1, next_(Tab, ets:first(shard_name(Tab, NextShard)), NextShard); next_(_, '$end_of_table', _) -> '$end_of_table'; next_(_, Key2, _) -> Key2. %% @equiv prev(Tab, Key1, shards_state:new()) prev(Tab, Key1) -> prev(Tab, Key1, shards_state:new()). %% @doc %% This operation behaves similar to `ets:prev/2'. %% The order in which results are returned, might be not the same %% as the original ETS function. Remember shards architecture %% described at the beginning. %% %% @see ets:prev/2. %% @end -spec prev(Tab, Key1, State) -> Key2 | '$end_of_table' when Tab :: atom(), Key1 :: term(), State :: shards_state:state(), Key2 :: term(). prev(Tab, Key1, State) -> case ets:info(shard_name(Tab, 0), type) of ordered_set -> ets:prev(shard_name(Tab, 0), Key1); _ -> next(Tab, Key1, State) end. %% @equiv select(Tab, MatchSpec, shards_state:new()) select(Tab, MatchSpec) -> select(Tab, MatchSpec, shards_state:new()). %% @doc %% If 3rd argument is `pos_integer()' this function behaves like %% `ets:select/3', but if it is the `shards_state:state()', %% it behaves like `ets:select/2'. %% %% The order in which results are returned, might be not the same %% as the original ETS function. Remember shards architecture %% described at the beginning. %% %% @see ets:select/2. %% @see ets:select/3. %% @end -spec select(Tab, MatchSpec, StateOrLimit) -> Response when Tab :: atom(), MatchSpec :: ets:match_spec(), StateOrLimit :: shards_state:state() | pos_integer(), Match :: term(), ResWithState :: [Match], ResWithLimit :: {[Match], Continuation} | '$end_of_table', Continuation :: continuation(), Response :: ResWithState | ResWithLimit. select(Tab, MatchSpec, Limit) when is_integer(Limit), Limit > 0 -> select(Tab, MatchSpec, Limit, shards_state:new()); select(Tab, MatchSpec, State) -> Map = {fun ets:select/2, [MatchSpec]}, Reduce = fun lists:append/2, mapred(Tab, Map, Reduce, State). %% @doc %% This operation behaves similar to `ets:select/3'. %% The order in which results are returned, might be not the same %% as the original ETS function. Remember shards architecture %% described at the beginning. %% %% @see ets:select/3. %% @end -spec select(Tab, MatchSpec, Limit, State) -> Response when Tab :: atom(), MatchSpec :: ets:match_spec(), Limit :: pos_integer(), State :: shards_state:state(), Match :: term(), Continuation :: continuation(), Response :: {[Match], Continuation} | '$end_of_table'. select(Tab, MatchSpec, Limit, State) -> N = shards_state:n_shards(State), q(select, Tab, MatchSpec, Limit, q_fun(), Limit, N - 1, {[], nil}). %% @doc %% This operation behaves similar to `ets:select/1'. %% The order in which results are returned, might be not the same %% as the original ETS function. Remember shards architecture %% described at the beginning. %% %% @see ets:select/1. %% @end -spec select(Continuation) -> Response when Match :: term(), Continuation :: continuation(), Response :: {[Match], Continuation} | '$end_of_table'. select({_, _, Limit, _, _} = Continuation) -> q(select, Continuation, q_fun(), Limit, []). %% @equiv select_count(Tab, MatchSpec, shards_state:new()) select_count(Tab, MatchSpec) -> select_count(Tab, MatchSpec, shards_state:new()). %% @doc %% This operation behaves like `ets:select_count/2'. %% %% @see ets:select_count/2. %% @end -spec select_count(Tab, MatchSpec, State) -> NumMatched when Tab :: atom(), MatchSpec :: ets:match_spec(), State :: shards_state:state(), NumMatched :: non_neg_integer(). select_count(Tab, MatchSpec, State) -> Map = {fun ets:select_count/2, [MatchSpec]}, Reduce = {fun(Res, Acc) -> Acc + Res end, 0}, mapred(Tab, Map, Reduce, State). %% @equiv select_delete(Tab, MatchSpec, shards_state:new()) select_delete(Tab, MatchSpec) -> select_delete(Tab, MatchSpec, shards_state:new()). %% @doc %% This operation behaves like `ets:select_delete/2'. %% %% @see ets:select_delete/2. %% @end -spec select_delete(Tab, MatchSpec, State) -> NumDeleted when Tab :: atom(), MatchSpec :: ets:match_spec(), State :: shards_state:state(), NumDeleted :: non_neg_integer(). select_delete(Tab, MatchSpec, State) -> Map = {fun ets:select_delete/2, [MatchSpec]}, Reduce = {fun(Res, Acc) -> Acc + Res end, 0}, mapred(Tab, Map, Reduce, State). %% @equiv select_reverse(Tab, MatchSpec, shards_state:new()) select_reverse(Tab, MatchSpec) -> select_reverse(Tab, MatchSpec, shards_state:new()). %% @doc %% If 3rd argument is `pos_integer()' this function behaves like %% `ets:select_reverse/3', but if it is the `shards_state:state()', %% it behaves like `ets:select_reverse/2'. %% %% The order in which results are returned, might be not the same %% as the original ETS function. Remember shards architecture %% described at the beginning. %% %% @see ets:select_reverse/2. %% @see ets:select_reverse/3. %% @end -spec select_reverse(Tab, MatchSpec, StateOrLimit) -> Response when Tab :: atom(), MatchSpec :: ets:match_spec(), StateOrLimit :: shards_state:state() | pos_integer(), Match :: term(), ResWithState :: [Match], ResWithLimit :: {[Match], Continuation} | '$end_of_table', Continuation :: continuation(), Response :: ResWithState | ResWithLimit. select_reverse(Tab, MatchSpec, Limit) when is_integer(Limit), Limit > 0 -> select_reverse(Tab, MatchSpec, Limit, shards_state:new()); select_reverse(Tab, MatchSpec, State) -> Map = {fun ets:select_reverse/2, [MatchSpec]}, Reduce = fun lists:append/2, mapred(Tab, Map, Reduce, State). %% @doc %% This operation behaves similar to `ets:select_reverse/3'. %% The order in which results are returned, might be not the same %% as the original ETS function. Remember shards architecture %% described at the beginning. %% %% @see ets:select_reverse/3. %% @end -spec select_reverse(Tab, MatchSpec, Limit, State) -> Response when Tab :: atom(), MatchSpec :: ets:match_spec(), Limit :: pos_integer(), State :: shards_state:state(), Match :: term(), Continuation :: continuation(), Response :: {[Match], Continuation} | '$end_of_table'. select_reverse(Tab, MatchSpec, Limit, State) -> N = shards_state:n_shards(State), q(select_reverse, Tab, MatchSpec, Limit, q_fun(), Limit, N - 1, {[], nil}). %% @doc %% This operation behaves similar to `ets:select_reverse/1'. %% The order in which results are returned, might be not the same %% as the original ETS function. Remember shards architecture %% described at the beginning. %% %% @see ets:select_reverse/1. %% @end -spec select_reverse(Continuation) -> Response when Match :: term(), Continuation :: continuation(), Response :: {[Match], Continuation} | '$end_of_table'. select_reverse({_, _, Limit, _, _} = Continuation) -> q(select_reverse, Continuation, q_fun(), Limit, []). %% @equiv setopts(Tab, Opts, shards_state:new()) setopts(Tab, Opts) -> setopts(Tab, Opts, shards_state:new()). %% @doc %% Equivalent to `ets:setopts/2' for each shard table. It returns %% a `boolean()' instead that just `true'. Returns `true' if the %% function was applied successfully on each shard, otherwise %% `false' is returned. %% %% @see ets:setopts/2. %% @end -spec setopts(Tab, Opts, State) -> boolean() when Tab :: atom(), Opts :: Opt | [Opt], Opt :: {heir, pid(), HeirData} | {heir, none}, HeirData :: term(), State :: shards_state:state(). setopts(Tab, Opts, State) -> Map = {fun shards_owner:setopts/2, [Opts]}, Reduce = {fun(E, Acc) -> Acc and E end, true}, mapred(Tab, Map, Reduce, State). %% @equiv tab2file(Tab, Filenames, shards_state:new()) tab2file(Tab, Filenames) -> tab2file(Tab, Filenames, shards_state:new()). %% @equiv tab2file/4 tab2file(Tab, Filenames, Options) when is_list(Options) -> tab2file(Tab, Filenames, Options, shards_state:new()); tab2file(Tab, Filenames, State) -> tab2file(Tab, Filenames, [], State). %% @doc %% Similar to `ets:tab2file/3', but it returns a list of %% responses for each shard table instead. %% %% @see ets:tab2file/3. %% @end -spec tab2file(Tab, Filenames, Options, State) -> Response when Tab :: atom(), Filenames :: [file:name()], Options :: [Option], Option :: {extended_info, [ExtInfo]} | {sync, boolean()}, ExtInfo :: md5sum | object_count, State :: shards_state:state(), ShardTab :: atom(), ShardRes :: ok | {error, Reason :: term()}, Response :: [{ShardTab, ShardRes}]. tab2file(Tab, Filenames, Options, State) -> N = shards_state:n_shards(State), [begin ets:tab2file(Shard, Filename, Options) end || {Shard, Filename} <- lists:zip(list(Tab, N), Filenames)]. %% @equiv tab2list(Tab, shards_state:new()) tab2list(Tab) -> tab2list(Tab, shards_state:new()). %% @doc %% This operation behaves like `ets:tab2list/1'. %% %% @see ets:tab2list/1. %% @end -spec tab2list(Tab, State) -> [Object] when Tab :: atom(), State :: shards_state:state(), Object :: tuple(). tab2list(Tab, State) -> mapred(Tab, fun ets:tab2list/1, fun lists:append/2, State). %% @equiv ets:tabfile_info(Filename) tabfile_info(Filename) -> ets:tabfile_info(Filename). %% @equiv table(Tab, shards_state:new()) table(Tab) -> table(Tab, shards_state:new()). %% @equiv table/3 table(Tab, Options) when is_list(Options) -> table(Tab, Options, shards_state:new()); table(Tab, State) -> table(Tab, [], State). %% @doc %% Similar to `ets:table/2', but it returns a list of `ets:table/2' %% responses, one for each shard table. %% %% @see ets:table/2. %% @end -spec table(Tab, Options, State) -> [QueryHandle] when Tab :: atom(), QueryHandle :: qlc:query_handle(), Options :: [Option] | Option, Option :: {n_objects, NObjects} | {traverse, TraverseMethod}, NObjects :: default | pos_integer(), State :: shards_state:state(), MatchSpec :: ets:match_spec(), TraverseMethod :: first_next | last_prev | select | {select, MatchSpec}. table(Tab, Options, State) -> mapred(Tab, {fun ets:table/2, [Options]}, State). %% @equiv ets:test_ms(Tuple, MatchSpec) test_ms(Tuple, MatchSpec) -> ets:test_ms(Tuple, MatchSpec). %% @equiv take(Tab, Key, shards_state:new()) take(Tab, Key) -> take(Tab, Key, shards_state:new()). %% @doc %% This operation behaves like `ets:take/2'. %% %% @see ets:take/2. %% @end -spec take(Tab, Key, State) -> [Object] when Tab :: atom(), Key :: term(), State :: shards_state:state(), Object :: tuple(). take(Tab, Key, State) -> Map = {fun ets:take/2, [Key]}, Reduce = fun lists:append/2, mapred(Tab, Key, Map, Reduce, State, r). %% @equiv update_counter(Tab, Key, UpdateOp, shards_state:new()) update_counter(Tab, Key, UpdateOp) -> update_counter(Tab, Key, UpdateOp, shards_state:new()). %% @doc %% This operation behaves like `ets:update_counter/3'. %% %% @see ets:update_counter/3. %% @end -spec update_counter(Tab, Key, UpdateOp, State) -> Result when Tab :: atom(), Key :: term(), UpdateOp :: term(), State :: shards_state:state(), Result :: integer(). update_counter(Tab, Key, UpdateOp, State) -> Map = {fun ets:update_counter/3, [Key, UpdateOp]}, mapred(Tab, Key, Map, nil, State, w). %% @doc %% This operation behaves like `ets:update_counter/4'. %% %% @see ets:update_counter/4. %% @end -spec update_counter(Tab, Key, UpdateOp, Default, State) -> Result when Tab :: atom(), Key :: term(), UpdateOp :: term(), Default :: tuple(), State :: shards_state:state(), Result :: integer(). update_counter(Tab, Key, UpdateOp, Default, State) -> Map = {fun ets:update_counter/4, [Key, UpdateOp, Default]}, mapred(Tab, Key, Map, nil, State, w). %% @equiv update_element(Tab, Key, ElementSpec, shards_state:new()) update_element(Tab, Key, ElementSpec) -> update_element(Tab, Key, ElementSpec, shards_state:new()). %% @doc %% This operation behaves like `ets:update_element/3'. %% %% @see ets:update_element/3. %% @end -spec update_element(Tab, Key, ElementSpec, State) -> boolean() when Tab :: atom(), Key :: term(), Pos :: pos_integer(), Value :: term(), ElementSpec :: {Pos, Value} | [{Pos, Value}], State :: shards_state:state(). update_element(Tab, Key, ElementSpec, State) -> Map = {fun ets:update_element/3, [Key, ElementSpec]}, mapred(Tab, Key, Map, nil, State, w). %%%=================================================================== %%% Extended API %%%=================================================================== %% @doc %% Builds a shard name `ShardName'. %%