diff options
Diffstat (limited to 'src/replication.erl')
-rw-r--r-- | src/replication.erl | 165 |
1 files changed, 0 insertions, 165 deletions
diff --git a/src/replication.erl b/src/replication.erl deleted file mode 100644 index 96be0ad3..00000000 --- a/src/replication.erl +++ /dev/null @@ -1,165 +0,0 @@ -%%%------------------------------------------------------------------- -%%% File: replication.erl -%%% @author Brad Anderson <brad@cloudant.com> [http://www.cloudant.com] -%%% @copyright 2009 Brad Anderson -%%% @doc -%%% -%%% @end -%%% -%%% @since 2009-06-14 by Brad Anderson -%%%------------------------------------------------------------------- --module(replication). --author('brad@cloudant.com'). - -%% API --export([partners/2, partners/3, partners_plus/2]). - --include_lib("eunit/include/eunit.hrl"). --include("../include/config.hrl"). --include("../include/common.hrl"). - - -%%==================================================================== -%% API -%%==================================================================== - -partners(Node, Nodes) -> - partners(Node, Nodes, configuration:get_config()). - - -%%-------------------------------------------------------------------- -%% @spec partners(Node::atom(), Nodes::list(), Config::config()) -> -%% list() -%% @doc returns the list of all replication partners for the specified node -%% @end -%%-------------------------------------------------------------------- -partners(Node, Nodes, Config) -> - N = Config#config.n, - Meta = Config#config.meta, - pick_partners(Meta, Node, Nodes, [], N - 1). - - -%% return a list of live/up Partners, and if all Partners are down, -%% walk the ring to get one other remote node and return it. -partners_plus(Node, Nodes) -> - Partners = partners(Node, Nodes), - PartnersDown = lists:subtract(Partners, erlang:nodes()), - PartnersUp = lists:subtract(Partners, PartnersDown), - case PartnersUp of - [] -> - TargetNodes = target_list(Node, Nodes), - NonPartners = lists:subtract(TargetNodes, - lists:flatten([Node, Partners])), - walk_ring(NonPartners); - _ -> - %% at least one partner is up, so gossip w/ them - PartnersUp - end. - - -%%==================================================================== -%% Internal functions -%%==================================================================== - -%% @spec pick_partners(proplist(), Node::dynomite_node(), [Node], [Node], -%% integer()) -> list() -%% @doc iterate through N-1 partner picks, returning the resulting list sorted -pick_partners(_Meta, Node, _Nodes, Acc, 0) -> - lists:sort(lists:delete(Node, Acc)); -pick_partners(Meta, Node, Nodes, Acc, Count) -> - Partner = pick_partner(Meta, Node, Nodes, Acc, 1), - NewNodes = lists:filter(fun(Elem) -> - case Elem of - no_partner_found -> false; - Partner -> false; - _ -> true - end - end, Nodes), - NewAcc = case Partner of - no_partner_found -> Acc; - _ -> [Partner|Acc] - end, - pick_partners(Meta, Node, NewNodes, NewAcc, Count-1). - - -%% @spec pick_partner(proplist(), Node::dynomite_node(), [Node], [Node], -%% integer()) -> Node::dynomite_node() -%% @doc pick a specific replication partner at the given level -pick_partner([], Node, Nodes, _Acc, 1) -> - %% handle the no metadata situation - %% Note: This clause must be before the Level > length(Meta) guarded clause - target_key(node:name(Node), lists:map(fun node:name/1, Nodes), roundrobin); - -pick_partner(Meta, _Node, _Nodes, Acc, Level) when Level > length(Meta) -> - Acc; - -pick_partner(Meta, Node, Nodes, Acc, Level) -> - MetaDict = meta_dict(Nodes, Level, dict:new()), - NodeKey = lists:sublist(node:attributes(Node), Level), - Keys = dict:fetch_keys(MetaDict), - {_MetaName, Strategy} = lists:nth(Level, Meta), - TargetKey = target_key(NodeKey, Keys, Strategy), - Candidates = dict:fetch(TargetKey, MetaDict), - case length(Candidates) of - 0 -> - %% didn't find a candidate - no_partner_found; - 1 -> - %% found only one candidate, return it - [Partner] = Candidates, - Partner; - _ -> - pick_partner(Meta, Node, Nodes, Acc, Level + 1) - end. - - -%% @doc construct a dict that holds the key of metadata values so far (up to -%% the current level, and dynomite_node() list as the value. This is used -%% to select a partner in pick_partner/5 -%% @end -meta_dict([], _Level, Dict) -> - Dict; - -meta_dict([Node|Rest], Level, Dict) -> - Key = lists:sublist(node:attributes(Node), Level), - DictNew = dict:append(Key, Node, Dict), - meta_dict(Rest, Level, DictNew). - - -%% @spec target_key(term(), list(), Strategy::atom()) -> term() -%% @doc given the key and keys, sort the list of keys based on stragety (i.e. -%% for roundrobin, sort them, put the NodeKey on the end of the list, and -%% then return the head of the list as the target. -%% @end -%% TODO: moar strategies other than roundrobin? -target_key(NodeKey, Keys, roundrobin) -> - SortedKeys = lists:sort(Keys), - TargetKey = case target_list(NodeKey, SortedKeys) of - [] -> no_partner_found; - [Key|_Rest] -> Key - end, - TargetKey. - - -%% @spec target_list(term(), list()) -> list() -%% @doc split the list of keys into 'lessthan NodeKey', NodeKey, and 'greaterthan -%% Nodekey' and then put the lessthan section on the end of the list -%% @end -target_list(_NodeKey, []) -> - []; -target_list(NodeKey, Keys) -> - {A, [NodeKey|B]} = lists:splitwith(fun(K) -> K /= NodeKey end, Keys), - lists:append([B, A, [NodeKey]]). - - -walk_ring([]) -> - %% TODO: should we be more forceful here and throw? not for now - showroom_log:message(info, - "~p:walk_ring/1 - could not find node for gossip", [?MODULE]), - []; - -walk_ring([Node|Rest]) -> - case lists:member(Node, erlang:nodes()) of - true -> [Node]; - _ -> walk_ring(Rest) - end. |