1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
|
-module(mem3).
-export([start/0, stop/0, restart/0, state/0, nodes/0, shards/1, shards/2,
choose_shards/2]).
-include("mem3.hrl").
-define(SERVER, mem3_server).
start() ->
application:start(mem3).
stop() ->
application:stop(mem3).
restart() ->
stop(),
start().
%% @doc Detailed report of cluster-wide membership state. Queries the state
%% on all member nodes and builds a dictionary with unique states as the
%% key and the nodes holding that state as the value. Also reports member
%% nodes which fail to respond and nodes which are connected but are not
%% cluster members. Useful for debugging.
-spec state() -> [{any | bad_nodes | non_member_nodes, [node()]}].
state() ->
{ok, Nodes} = mem3:nodes(),
AllNodes = erlang:nodes([this, visible]),
{Replies, BadNodes} = gen_server:multi_call(Nodes, ?SERVER, state),
Dict = lists:foldl(fun({Node, {ok,State}}, D) ->
orddict:append(State, Node, D)
end, orddict:new(), Replies),
[{non_member_nodes, AllNodes -- Nodes}, {bad_nodes, BadNodes} | Dict].
-spec nodes() -> [node()].
nodes() ->
mem3_nodes:get_nodelist().
-spec shards(DbName::binary()) -> [#shard{}].
shards(DbName) ->
case ets:lookup(partitions, DbName) of
[] ->
% TODO fall back to checking dbs.couch directly
erlang:error(database_does_not_exist);
Else ->
Else
end.
-spec shards(DbName::binary(), DocId::binary()) -> [#shard{}].
shards(DbName, DocId) ->
HashKey = mem3_util:hash(DocId),
Head = #shard{
name = '_',
node = '_',
dbname = DbName,
range = ['$1','$2'],
ref = '_'
},
% TODO these conditions assume A < B, which we don't require
Conditions = [{'<', '$1', HashKey}, {'=<', HashKey, '$2'}],
case ets:select(partitions, [{Head, Conditions, ['$_']}]) of
[] ->
% TODO fall back to checking dbs.couch directly
erlang:error(database_does_not_exist);
Shards ->
Shards
end.
choose_shards(DbName, Options) ->
try shards(DbName)
catch error:database_does_not_exist ->
Nodes = mem3:nodes(),
NodeCount = length(Nodes),
N = mem3_util:n_val(couch_util:get_value(n, Options), NodeCount),
Q = mem3_util:to_integer(couch_util:get_value(q, Options,
couch_config:get("cluster", "q", "8"))),
% rotate to a random entry in the nodelist for even distribution
{A, B} = lists:split(crypto:rand_uniform(1,length(Nodes)+1), Nodes),
RotatedNodes = B ++ A,
mem3_util:create_partition_map(DbName, N, Q, RotatedNodes)
end.
|