Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: basho/riak_core
base: feuerlabs-stat-combo
...
head fork: basho/riak_core
compare: rz-klm-coverage-plan-refactor
Checking mergeability… Don't worry, you can still create the pull request.
  • 7 commits
  • 2 files changed
  • 0 commit comments
  • 2 contributors
Showing with 302 additions and 200 deletions.
  1. +13 −12 src/riak_core_coverage_fsm.erl
  2. +289 −188 src/riak_core_coverage_plan.erl
View
25 src/riak_core_coverage_fsm.erl
@@ -36,7 +36,7 @@
%% a 5 member tuple from their init function that looks
%% like this:
%%
-%% `{Request, VNodeSelector, NVal, PrimaryVNodeCoverage,
+%% `{Request, VNodeConstraint, NVal, Primaries,
%% NodeCheckService, VNodeMaster, Timeout, State}'
%%
%% The description of the tuple members is as follows:
@@ -44,14 +44,14 @@
%% <ul>
%% <li>Request - An opaque data structure that is used by
%% the VNode to implement the specific coverage request.</li>
-%% <li>VNodeSelector - Either the atom `all' to indicate that
+%% <li>VNodeConstraint - Either the atom `all' to indicate that
%% enough VNodes must be available to achieve a minimal
%% covering set or `allup' to use whatever VNodes are
%% available even if they do not represent a fully covering
%% set.</li>
%% <li>NVal - Indicates the replication factor and is used to
%% accurately create a minimal covering set of VNodes.</li>
-%% <li>PrimaryVNodeCoverage - The number of primary VNodes
+%% <li>Primaries - The number of primary VNodes
%% from the preference list to use in creating the coverage
%% plan.</li>
%% <li>NodeCheckService - The service to use to check for available
@@ -106,7 +106,7 @@ behaviour_info(_) ->
n_val :: pos_integer(),
node_check_service :: module(),
vnode_selector :: all | allup,
- pvc :: all | pos_integer(), % primary vnode coverage
+ primaries :: all | pos_integer(), % primary vnode coverage
request :: tuple(),
req_id :: req_id(),
required_responses :: pos_integer(),
@@ -153,15 +153,15 @@ test_link(Mod, From, RequestArgs, _Options, StateProps) ->
init([Mod,
From={_, ReqId, _},
RequestArgs]) ->
- {Request, VNodeSelector, NVal, PrimaryVNodeCoverage,
+ {Request, VNodeConstraint, NVal, Primaries,
NodeCheckService, VNodeMaster, Timeout, ModState} =
Mod:init(From, RequestArgs),
StateData = #state{mod=Mod,
mod_state=ModState,
node_check_service=NodeCheckService,
- vnode_selector=VNodeSelector,
+ vnode_selector=VNodeConstraint,
n_val=NVal,
- pvc = PrimaryVNodeCoverage,
+ primaries=Primaries,
request=Request,
req_id=ReqId,
timeout=Timeout,
@@ -189,16 +189,17 @@ initialize(timeout, StateData0=#state{mod=Mod,
mod_state=ModState,
n_val=NVal,
node_check_service=NodeCheckService,
- vnode_selector=VNodeSelector,
- pvc=PVC,
+ vnode_selector=VNodeConstraint,
+ primaries=Primaries,
request=Request,
req_id=ReqId,
timeout=Timeout,
vnode_master=VNodeMaster}) ->
- CoveragePlan = riak_core_coverage_plan:create_plan(VNodeSelector,
+ Offset = ReqId rem NVal,
+ CoveragePlan = riak_core_coverage_plan:create_plan(VNodeConstraint,
NVal,
- PVC,
- ReqId,
+ Primaries,
+ Offset,
NodeCheckService),
case CoveragePlan of
{error, Reason} ->
View
477 src/riak_core_coverage_plan.erl
@@ -1,8 +1,6 @@
%% -------------------------------------------------------------------
%%
-%% riak_core_coverage_plan: Create a plan to cover a minimal set of VNodes.
-%%
-%% Copyright (c) 2007-2011 Basho Technologies, Inc. All Rights Reserved.
+%% Copyright (c) 2007-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
@@ -21,36 +19,60 @@
%% -------------------------------------------------------------------
%% @doc A module to calculate a plan to cover a minimal set of VNodes.
-%% There is also an option to specify a number of primary VNodes
-%% from each preference list to use in the plan.
+%% There is also an option to specify a number of primary VNodes from
+%% each preference list to use in the plan.
-module(riak_core_coverage_plan).
%% API
--export([create_plan/5]).
+-export([create_plan/5,
+ create_plan/6,
+ print_coverage_plan/1]).
-type index() :: non_neg_integer().
--type req_id() :: non_neg_integer().
--type coverage_vnodes() :: [{index(), node()}].
+-type vnode() :: {index(), node()}.
+-type coverage_vnodes() :: [vnode()].
-type vnode_filters() :: [{node(), [{index(), [index()]}]}].
-type coverage_plan() :: {coverage_vnodes(), vnode_filters()}.
+-record(context, {keyspaces :: [non_neg_integer()],
+ partition_count :: pos_integer(),
+ offset :: non_neg_integer(),
+ n_val :: pos_integer(),
+ minimization_target :: nodes | vnodes,
+ unavail_keyspaces :: [non_neg_integer()],
+ primaries :: all | pos_integer(),
+ results=[] :: [vnode()],
+ ring :: term(),
+ index_increment :: pos_integer(),
+ vnode_fun :: function(),
+ vnode_constraint :: all | allup
+ }).
+
%% ===================================================================
%% Public API
%% ===================================================================
+%% @doc Create a coverage plan to distribute work to a set of
+%% covering VNodes around the ring.
+-spec create_plan(all | allup, pos_integer(), pos_integer(),
+ non_neg_integer(), atom()) ->
+ {error, term()} | coverage_plan().
+create_plan(VNodeConstraint, NVal, Primaries, Offset, Service) ->
+ create_plan(VNodeConstraint, NVal, Primaries, Offset, Service, vnodes).
+
%% @doc Create a coverage plan to distribute work to a set
-%% covering VNodes around the ring.
--spec create_plan(all | allup, pos_integer(), pos_integer(),
- req_id(), atom()) ->
+%% covering VNodes around the ring.
+-spec create_plan(all | allup,
+ pos_integer(),
+ pos_integer(),
+ non_neg_integer(),
+ atom(),
+ nodes | vnodes) ->
{error, term()} | coverage_plan().
-create_plan(VNodeSelector, NVal, PVC, ReqId, Service) ->
+create_plan(VNodeConstraint, NVal, Primaries, Offset, Service, MinimizationTarget) ->
{ok, Ring} = riak_core_ring_manager:get_my_ring(),
PartitionCount = riak_core_ring:num_partitions(Ring),
- %% Get the list of all nodes and the list of available
- %% nodes so we can have a list of unavailable nodes
- %% while creating a coverage plan.
- Nodes = riak_core_ring:all_members(Ring),
%% Check which nodes are up for the specified service
%% so we can determine which VNodes are ineligible
%% to be part of the coverage plan.
@@ -58,210 +80,289 @@ create_plan(VNodeSelector, NVal, PVC, ReqId, Service) ->
%% Create a coverage plan with the requested primary
%% preference list VNode coverage.
%% Get a list of the VNodes owned by any unavailble nodes
- DownVNodes = [Index ||
- {Index, Node}
- <- riak_core_ring:all_owners(Ring),
- lists:member(Node, (Nodes -- UpNodes))],
- %% Calculate an offset based on the request id to offer
- %% the possibility of different sets of VNodes being
- %% used even when all nodes are available.
- Offset = ReqId rem NVal,
-
+ DownVNodes = [Index || {Index, Node} <- riak_core_ring:all_owners(Ring),
+ not lists:member(Node, UpNodes)],
RingIndexInc = chash:ring_increment(PartitionCount),
- AllKeySpaces = lists:seq(0, PartitionCount - 1),
- UnavailableKeySpaces = [(DownVNode div RingIndexInc) || DownVNode <- DownVNodes],
- %% Create function to map coverage keyspaces to
- %% actual VNode indexes and determine which VNode
- %% indexes should be filtered.
- CoverageVNodeFun =
- fun({Position, KeySpaces}, Acc) ->
- %% Calculate the VNode index using the
- %% ring position and the increment of
- %% ring index values.
- VNodeIndex = (Position rem PartitionCount) * RingIndexInc,
- Node = riak_core_ring:index_owner(Ring, VNodeIndex),
- CoverageVNode = {VNodeIndex, Node},
- case length(KeySpaces) < NVal of
- true ->
- %% Get the VNode index of each keyspace to
- %% use to filter results from this VNode.
- KeySpaceIndexes = [(((KeySpaceIndex+1) rem
- PartitionCount) * RingIndexInc) ||
- KeySpaceIndex <- KeySpaces],
- {CoverageVNode, [{VNodeIndex, KeySpaceIndexes} | Acc]};
- false ->
- {CoverageVNode, Acc}
- end
- end,
%% The offset value serves as a tiebreaker in the
%% compare_next_vnode function and is used to distribute
%% work to different sets of VNodes.
- CoverageResult = find_coverage(AllKeySpaces,
- Offset,
- NVal,
- PartitionCount,
- UnavailableKeySpaces,
- lists:min([PVC, NVal]),
- []),
- case CoverageResult of
- {ok, CoveragePlan} ->
- %% Assemble the data structures required for
- %% executing the coverage operation.
- lists:mapfoldl(CoverageVNodeFun, [], CoveragePlan);
- {insufficient_vnodes_available, _KeySpace, PartialCoverage} ->
- case VNodeSelector of
- allup ->
- %% The allup indicator means generate a coverage plan
- %% for any available VNodes.
- lists:mapfoldl(CoverageVNodeFun, [], PartialCoverage);
- all ->
- {error, insufficient_vnodes_available}
- end
- end.
+
+ %% Create a coverage context
+ Context = #context{keyspaces=lists:seq(0, PartitionCount - 1),
+ offset=Offset,
+ n_val=NVal,
+ partition_count=PartitionCount,
+ minimization_target=MinimizationTarget,
+ unavail_keyspaces=[(DownVNode div RingIndexInc) ||
+ DownVNode <- DownVNodes],
+ primaries=lists:min([Primaries, NVal]),
+ ring=Ring,
+ index_increment=RingIndexInc,
+ vnode_constraint=VNodeConstraint,
+ vnode_fun=vnode_fun(Ring, RingIndexInc, PartitionCount)},
+
+ CoverageResult = find_coverage(Context, Context#context.primaries),
+ %% find_coverage(AllKeySpaces,
+ %% Offset,
+ %% NVal,
+ %% PartitionCount,
+ %% MinimizationTarget,
+ %% [(DownVNode div RingIndexInc) ||
+ %% DownVNode <- DownVNodes], % Unavail keyspaces
+ %% lists:min([Primaries, NVal]),
+ %% [],
+ %% vnode_fun(Ring, RingIndexInc, PartitionCount)),
+ handle_coverage_result(
+ CoverageResult,
+ VNodeConstraint,
+ Ring,
+ RingIndexInc,
+ NVal,
+ PartitionCount).
+
+handle_coverage_result({ok, CoveragePlan},
+ _,
+ Ring,
+ RingIndexInc,
+ NVal,
+ PartitionCount) ->
+ %% Assemble the data structures required for
+ %% executing the coverage operation.
+ FP = lists:mapfoldl(coverage_vnode_fun(Ring, RingIndexInc, NVal, PartitionCount),
+ [],
+ CoveragePlan),
+ print_coverage_plan(FP),
+ FP;
+handle_coverage_result({insufficient_vnodes, _KeySpace, PartialCoverage},
+ allup,
+ Ring,
+ RingIndexInc,
+ NVal,
+ PartitionCount) ->
+ %% The allup indicator means generate a coverage plan
+ %% for any available VNodes.
+ lists:mapfoldl(coverage_vnode_fun(Ring, RingIndexInc, NVal, PartitionCount),
+ [],
+ PartialCoverage);
+handle_coverage_result({insufficient_vnodes, KeySpace, PartialCoverage},
+ all,
+ _, _, _, _) ->
+ lager:warning("Insufficient vnodes available to achieve cluster coverage."
+ "Keyspace: ~p"
+ "Partial coverage plan: ~p",
+ [KeySpace, PartialCoverage]),
+ {error, insufficient_vnodes_available}.
+
+print_coverage_plan({Vnodes, _Filters}) ->
+ SortedVnodes = lists:sort(fun sort_by_node/2, Vnodes),
+ io:format("Coverage Plan~n"),
+ [io:format("~p~n", [Vnode]) || Vnode <- SortedVnodes],
+ io:format("~n").
%% ====================================================================
%% Internal functions
%% ====================================================================
+sort_by_node({Index1, Node}, {Index2, Node}) ->
+ Index1 < Index2;
+sort_by_node({_, Node1}, {_, Node2}) ->
+ Node1 < Node2.
+
+%% @doc Return a function to map coverage keyspaces to actual VNode
+%% indexes and determine which VNode indexes should be filtered.
+coverage_vnode_fun(Ring, RingIndexInc, NVal, PartitionCount) ->
+ fun({Position, KeySpaces}, Acc) when length(KeySpaces) < NVal ->
+ %% Get the VNode index of each keyspace to
+ %% use to filter results from this VNode.
+ KeySpaceIndexes = [(((KeySpaceIndex+1) rem
+ PartitionCount) * RingIndexInc) ||
+ KeySpaceIndex <- KeySpaces],
+ {VNodeIndex, _} = VNode = get_vnode(Ring,
+ RingIndexInc,
+ PartitionCount,
+ Position),
+ {VNode, [{VNodeIndex, KeySpaceIndexes} | Acc]};
+ ({Position, _KeySpaces}, Acc) ->
+ VNode = get_vnode(Ring, RingIndexInc, PartitionCount, Position),
+ {VNode, Acc}
+
+ end.
+
+%% @doc Return a function to return a pair consisting of the Vnode
+%% index and the node based on a given ring position.
+-spec vnode_fun(term(), pos_integer(), pos_integer()) -> function().
+vnode_fun(Ring, RingIndexInc, PartitionCount) ->
+ fun(Position) ->
+ get_vnode(Ring, RingIndexInc, PartitionCount, Position)
+ end.
+
+%% @doc Return a pair consisting of the Vnode index and the node based
+%% on a given ring position.
+-spec get_vnode(term(), pos_integer(), pos_integer(), non_neg_integer()) -> vnode().
+get_vnode(Ring, RingIndexInc, PartitionCount, Position) ->
+ %% Calculate the VNode index using the ring position and the
+ %% increment of ring index values.
+ VNodeIndex = (Position rem PartitionCount) * RingIndexInc,
+ Node = riak_core_ring:index_owner(Ring, VNodeIndex),
+ {VNodeIndex, Node}.
+
%% @private
-find_coverage(AllKeySpaces, Offset, NVal, PartitionCount, UnavailableKeySpaces, PVC, []) ->
- %% Calculate the available keyspaces.
- AvailableKeySpaces = [{((VNode+Offset) rem PartitionCount),
- VNode,
- n_keyspaces(VNode, NVal, PartitionCount)}
- || VNode <- (AllKeySpaces -- UnavailableKeySpaces)],
- case find_coverage_vnodes(
- ordsets:from_list(AllKeySpaces),
- AvailableKeySpaces,
- []) of
- {ok, CoverageResults} ->
- case PVC of
- 1 ->
- {ok, CoverageResults};
- _ ->
- find_coverage(AllKeySpaces,
- Offset,
- NVal,
- PartitionCount,
- UnavailableKeySpaces,
- PVC-1,
- CoverageResults)
- end;
- Error ->
- Error
- end;
-find_coverage(AllKeySpaces,
- Offset,
- NVal,
- PartitionCount,
- UnavailableKeySpaces,
- PVC,
- ResultsAcc) ->
- %% Calculate the available keyspaces. The list of
- %% keyspaces for each vnode that have already been
- %% covered by the plan are subtracted from the complete
- %% list of keyspaces so that coverage plans that
- %% want to cover more one preflist vnode work out
+find_coverage(#context{results=Results}, 0) ->
+ {ok, Results};
+find_coverage(Context, PrimariesLeft) ->
+ #context{n_val=NVal,
+ partition_count=PartitionCount,
+ keyspaces=AllKeySpaces,
+ offset=Offset,
+ minimization_target=MinimizationTarget,
+ unavail_keyspaces=UnavailableKeySpaces,
+ primaries=Primaries,
+ results=ResultsAcc,
+ vnode_fun=VnodeFun} = Context,
+ %% Calculate the available keyspaces. The list of keyspaces for
+ %% each vnode that have already been covered by the plan are
+ %% subtracted from the complete list of keyspaces so that coverage
+ %% plans that want to cover more one preflist vnode work out
%% correctly.
- AvailableKeySpaces = [{((VNode+Offset) rem PartitionCount),
- VNode,
- n_keyspaces(VNode, NVal, PartitionCount) --
- proplists:get_value(VNode, ResultsAcc, [])}
- || VNode <- (AllKeySpaces -- UnavailableKeySpaces)],
- case find_coverage_vnodes(ordsets:from_list(AllKeySpaces),
- AvailableKeySpaces,
- ResultsAcc) of
+ AvailableKeySpaces =
+ [
+ {VNode,
+ n_keyspaces(VNode, NVal, PartitionCount) --
+ proplists:get_value(VNode, ResultsAcc, []),
+ (VNode+Offset) rem PartitionCount
+ }
+ || VNode <- (AllKeySpaces -- UnavailableKeySpaces)],
+ case handle_coverage_vnodes(
+ find_coverage_vnodes(ordsets:from_list(AllKeySpaces),
+ AvailableKeySpaces,
+ ResultsAcc,
+ VnodeFun,
+ orddict:new(),
+ MinimizationTarget), ResultsAcc) of
{ok, CoverageResults} ->
- UpdateResultsFun =
- fun({Key, NewValues}, Results) ->
- case proplists:get_value(Key, Results) of
- undefined ->
- [{Key, NewValues} | Results];
- Values ->
- UniqueValues = lists:usort(Values ++ NewValues),
- [{Key, UniqueValues} |
- proplists:delete(Key, Results)]
- end
- end,
- UpdatedResults =
- lists:foldl(UpdateResultsFun, ResultsAcc, CoverageResults),
- case PVC of
- 1 ->
- {ok, UpdatedResults};
- _ ->
- find_coverage(AllKeySpaces,
- Offset,
- NVal,
- PartitionCount,
- UnavailableKeySpaces,
- PVC-1,
- UpdatedResults)
- end;
+ find_coverage(Context#context{results=CoverageResults}, PrimariesLeft-1);
Error ->
Error
end.
+-spec handle_coverage_vnodes({ok, [vnode()]} |
+ {insufficient_vnodes, ordsets:new(), [vnode()]} |
+ {error, term()}, [vnode()]) ->
+ {ok, [vnode()]} |
+ {insufficient_vnodes, ordsets:new(), [vnode()]} |
+ {error, term()}.
+handle_coverage_vnodes({ok, _}=CoverageResults, []) ->
+ CoverageResults;
+handle_coverage_vnodes({ok, CoverageVnodes}, Acc) ->
+ {ok, lists:foldl(fun augment_coverage_results/2, Acc, CoverageVnodes)};
+handle_coverage_vnodes({insufficient_vnodes, _, _}=Result, _) ->
+ Result;
+handle_coverage_vnodes({error, _}=Error, _) ->
+ Error.
+
+augment_coverage_results({Key, NewValues}, Results) ->
+ case proplists:get_value(Key, Results) of
+ undefined ->
+ [{Key, NewValues} | Results];
+ Values ->
+ UniqueValues = lists:usort(Values ++ NewValues),
+ [{Key, UniqueValues} |
+ proplists:delete(Key, Results)]
+ end.
+
%% @private
%% @doc Find the N key spaces for a VNode
-n_keyspaces(VNode, N, PartitionCount) ->
- ordsets:from_list([X rem PartitionCount ||
- X <- lists:seq(PartitionCount + VNode - N,
- PartitionCount + VNode - 1)]).
+-spec n_keyspaces(non_neg_integer(), pos_integer(), pos_integer()) -> ordsets:new().
+n_keyspaces(VNodeIndex, N, PartitionCount) ->
+ LB = PartitionCount + VNodeIndex - N,
+ UB = PartitionCount + VNodeIndex - 1,
+ ordsets:from_list([X rem PartitionCount || X <- lists:seq(LB, UB)]).
%% @private
%% @doc Find a minimal set of covering VNodes
-find_coverage_vnodes([], _, Coverage) ->
+find_coverage_vnodes([], _, Coverage, _, _, _) ->
{ok, lists:sort(Coverage)};
-find_coverage_vnodes(KeySpace, [], Coverage) ->
- {insufficient_vnodes_available, KeySpace, lists:sort(Coverage)};
-find_coverage_vnodes(KeySpace, Available, Coverage) ->
- Res = next_vnode(KeySpace, Available),
- case Res of
- {0, _, _} -> % out of vnodes
- find_coverage_vnodes(KeySpace, [], Coverage);
- {_NumCovered, VNode, _} ->
- {value, {_, VNode, Covers}, UpdAvailable} = lists:keytake(VNode, 2, Available),
- UpdCoverage = [{VNode, ordsets:intersection(KeySpace, Covers)} | Coverage],
+find_coverage_vnodes(KeySpace, [], Coverage, _, _, _) ->
+ {insufficient_vnodes, KeySpace, lists:sort(Coverage)};
+find_coverage_vnodes(KeySpace, Available, Coverage, VnodeFun, NodeCounts, MinimizeFor) ->
+ {NumCovered, Position, TB, {_, Node}=_Vnode, _} =
+ next_vnode(
+ [{Pos, covers(KeySpace, CoversKeys), TB} ||
+ {Pos, CoversKeys, TB} <- Available],
+ VnodeFun,
+ NodeCounts,
+ MinimizeFor),
+ lager:debug("Next vnode: ~p ~p ~p ~p", [Position, TB, NumCovered, Node]),
+ UpdNodeCounts = orddict:update_counter(Node, 1, NodeCounts),
+ case NumCovered of
+ 0 -> % out of vnodes
+ find_coverage_vnodes(KeySpace, [], Coverage, VnodeFun, UpdNodeCounts, MinimizeFor);
+ _ ->
+ {value, {Position, Covers, _}, UpdAvailable} =
+ lists:keytake(Position, 1, Available),
+ lager:debug("KS: ~p Covers: ~p", [KeySpace, Covers]),
+ UpdCoverage = [{Position, ordsets:intersection(KeySpace, Covers)} | Coverage],
UpdKeySpace = ordsets:subtract(KeySpace, Covers),
- find_coverage_vnodes(UpdKeySpace, UpdAvailable, UpdCoverage)
+ find_coverage_vnodes(UpdKeySpace,
+ UpdAvailable,
+ UpdCoverage,
+ VnodeFun,
+ UpdNodeCounts,
+ MinimizeFor)
end.
%% @private
%% @doc Find the next vnode that covers the most of the
%% remaining keyspace. Use VNode id as tie breaker.
-next_vnode(KeySpace, Available) ->
- CoverCount = [{covers(KeySpace, CoversKeys), VNode, TieBreaker} ||
- {TieBreaker, VNode, CoversKeys} <- Available],
- hd(lists:sort(fun compare_next_vnode/2, CoverCount)).
+-spec next_vnode([{non_neg_integer(), [non_neg_integer()]}], function(), orddict:new(), nodes | vnodes) ->
+ non_neg_integer().
+next_vnode(EligibleVnodes, VnodeFun, NodeCounts, MinimizeFor) ->
+ lists:foldl(vnode_compare_fun(VnodeFun, NodeCounts, MinimizeFor), [], EligibleVnodes).
-%% @private
-%% There is a potential optimization here once
-%% the partition claim logic has been changed
-%% so that physical nodes claim partitions at
-%% regular intervals around the ring.
-%% The optimization is for the case
-%% when the partition count is not evenly divisible
-%% by the n_val and when the coverage counts of the
-%% two arguments are equal and a tiebreaker is
-%% required to determine the sort order. In this
-%% case, choosing the lower node for the final
-%% vnode to complete coverage will result
-%% in an extra physical node being involved
-%% in the coverage plan so the optimization is
-%% to choose the upper node to minimize the number
-%% of physical nodes.
-compare_next_vnode({CA, _VA, TBA}, {CB, _VB, TBB}) ->
- if
- CA > CB -> %% Descending sort on coverage
- true;
- CA < CB ->
- false;
- true ->
- TBA < TBB %% If equal coverage choose the lower node.
+vnode_compare_fun(VnodeFun, NodeCounts, vnodes) ->
+ fun({A, ACoverCount, ATB}, []) ->
+ VnodeA = VnodeFun(A),
+ {ACoverCount, A, ATB, VnodeA, NodeCounts};
+ ({A, ACoverCount, ATB}, {BCoverCount, B, BTB, {_, _NodeB}=VnodeB, _}) ->
+ VnodeA = VnodeFun(A),
+ compare_vnodes({ACoverCount, A, ATB, VnodeA, 0},
+ {BCoverCount, B, BTB, VnodeB, 0})
+ end;
+vnode_compare_fun(VnodeFun, NodeCounts, nodes) ->
+ fun({A, ACoverCount, ATB}, []) ->
+ VnodeA = VnodeFun(A),
+ {ACoverCount, A, ATB, VnodeA, NodeCounts};
+ ({A, ACoverCount, ATB}, {BCoverCount, B, BTB, {_, NodeB}=VnodeB, _}) ->
+ {_, NodeA} = VnodeA = VnodeFun(A),
+ compare_vnodes({ACoverCount, A, ATB, VnodeA, orddict:find(NodeA, NodeCounts)},
+ {BCoverCount, B, BTB, VnodeB, orddict:find(NodeB, NodeCounts)})
end.
+compare_vnodes({ACoverCount, _, _, _, _}=A, {BCoverCount, _, _, _, _})
+ when ACoverCount > BCoverCount ->
+ A;
+compare_vnodes({ACoverCount, _, _, _, _}, {BCoverCount, _, _, _, _}=B)
+ when ACoverCount < BCoverCount ->
+ B;
+compare_vnodes({_, _, ATB, _, NodeCount}=A, {_, _, BTB, _, NodeCount})
+ when ATB =< BTB ->
+ A;
+compare_vnodes({_, _, ATB, _, NodeCount}, {_, _, BTB, _, NodeCount}=B)
+ when ATB > BTB ->
+ B;
+compare_vnodes({_, _, _, _, error}, {_, _, _, _, _}=B) ->
+ B;
+compare_vnodes({_, _, _, _, _}=A, {_, _, _, _, error}) ->
+ A;
+compare_vnodes({_, _, _, _, {ok, ANodeCount}}=A, {_, _, _, _, {ok, BNodeCount}})
+ when ANodeCount >= BNodeCount ->
+ A;
+compare_vnodes({_, _, _, _, {ok, ANodeCount}}, {_, _, _, _, {ok, BNodeCount}}=B)
+ when ANodeCount < BNodeCount ->
+ B.
+
%% @private
%% @doc Count how many of CoversKeys appear in KeySpace
+-spec covers(ordsets:ordset(), ordsets:ordset()) -> non_neg_integer().
covers(KeySpace, CoversKeys) ->
ordsets:size(ordsets:intersection(KeySpace, CoversKeys)).
-

No commit comments for this range

Something went wrong with that request. Please try again.