-
Notifications
You must be signed in to change notification settings - Fork 0
/
manager.erl
120 lines (97 loc) · 3.52 KB
/
manager.erl
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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
-module (manager).
-behaviour (gen_server).
-define(SERVER, ?MODULE).
-export([start/1]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
start(Stores) ->
{_, Pid} = gen_server:start_link(?MODULE, Stores, []),
Pid.
init(Stores) -> {ok, Stores}.
% Callback Routines
handle_call({up, Key, Value}, _From, Stores) ->
Store = select_store(Stores, Key),
Response = gen_server:call(Store, {up, Key, Value}),
{reply, Response, Stores};
handle_call({read, Keys}, _From, Stores) ->
Values = process_reads(Stores, Keys),
{reply, Values, Stores};
handle_call({gc}, _From, Stores) ->
gc(Stores),
{reply, ok, Stores}.
handle_cast(_Msg, State) -> {noreply, State}.
handle_info(_Info, State) -> {noreply, State}.
terminate(_Reason, _State) -> ok.
code_change(_OldVsn, State, _Extra) -> {ok, State}.
%%----------------------------------------------------------------------
%% Function: process_reads/2
%% Purpose: Return a list of Values, acquired from the Store(s)
%% Args: Store pid
%% List of Keys
%% Returns: List of Values corresponding to asked Keys, order preserved
%%----------------------------------------------------------------------
process_reads(Stores, [Head|Tail]) ->
Store = select_store(Stores, Head),
[gen_server:call(Store, {read, timestamp(), Head}) | process_reads(Stores, Tail)];
process_reads(_, []) -> [].
%%----------------------------------------------------------------------
%% Function: gc/1
%% Purpose: Request a garbage collection on every stores.
%% Args: Stores, list of Stores
%% Returns: Request ok
%%--------------
gc([HeadStore | Stores]) ->
gen_server:call(HeadStore, {gc}),
gc(Stores);
gc([]) -> ok.
%%----------------------------------------------------------------------
%% Function: select_store/2
%% Purpose: From the list of Stores, elect the target Store to acces the Key
%% using a deterministic hash function.
%% Args: Stores, list of Stores
%% Key from the key/val pair
%% Returns: Elected Store
%%--------------
select_store(Stores, Key) ->
select_store(Stores, 1, length(Stores), Key, {0, -1}).
select_store(Stores, Elem, LastElem, Key, {Champion, Score}) ->
case Elem > LastElem of
true -> lists:nth(Champion, Stores);
false ->
NewScore = score(Elem, Key),
case NewScore > Score of
true -> select_store(Stores, Elem+1, LastElem, Key, {Elem, NewScore});
false -> select_store(Stores, Elem+1, LastElem, Key, {Champion, Score})
end
end.
%%----------------------------------------------------------------------
%% Function: score/2
%% Purpose: Compute a score based on a value of the Store and desired Key
%% Args: Position of the Store in list,
%% Key from the key/val pair
%% Returns: Score
%%--------------
score(Num, Key) ->
List = lists:concat([Num, Key]),
Hash = binary_to_list(crypto:hash(sha, List)),
Score = sum_list(Hash),
1.0 / math:log(Score).
%%----------------------------------------------------------------------
%% Function: sum_list/1
%% Purpose: Sums a list of integers
%% Args: List of integers
%% Returns: Sum
%%--------------
sum_list(List) ->
sum_list(List, 0).
sum_list([H|T], Acc) ->
sum_list(T, Acc+H);
sum_list([], Acc) ->
Acc.
%%----------------------------------------------------------------------
%% Function: timestamp/0
%% Purpose: Return system time as timestamp
%% Returns: timestamp
%%----------------------------------------------------------------------
timestamp() ->
os:system_time(millisecond).