-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathkey_db.erl
162 lines (126 loc) · 4.7 KB
/
key_db.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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
% key_db.erl
% Sam Weiss
% 7 December 2016
%
% This module implements a simplified interface for node/key storage
-module(key_db).
% public interface functions
-export([start_link/0, start_link/1, stop/0,
insert/1, get_key/1, get_nodes/0]).
-behaviour(gen_server).
-export([init/1, handle_call/3, handle_cast/2,
code_change/3, terminate/2, handle_info/2]).
% This file contains the schema that will be used for the Mnesia database
-include("key_db_schema.hrl").
%% ~~~~~~~~~~~~~~~~~~~~~~~~
%% API Function Definitions
%% ~~~~~~~~~~~~~~~~~~~~~~~~
% Start the server
% optionally specify whether the database should use mnesia or a list based
% in memory implementation (when mnesia isn't present)
% the default database backend is mnesia as it comes with most erlang installs
start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, mnesia, []).
start_link(in_mem) ->
gen_server:start_link({local, ?MODULE}, ?MODULE, in_mem, []);
start_link(mnesia) ->
gen_server:start_link({local, ?MODULE}, ?MODULE, mnesia, []).
% stop the database, for in memory databses this resets all data
stop() ->
gen_server:cast({global, ?MODULE}, stop).
%% ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
%% External Function Definitions
%% ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
% get the names of all the nodes current in the database
% if no nodes are registered this returns an empty list
get_nodes() ->
gen_server:call(?MODULE, get_nodes).
% get the key associated with a given node
% if no node by that name is registered this returns an empty list
get_key(Name) ->
gen_server:call(?MODULE, {get_key, Name}).
% associate a node with the provided key
% if that node already exists in the database then its value is replaced
insert({Name, Key}) ->
gen_server:cast(?MODULE, {insert_key, Name, Key}).
%% ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
%% gen_server Function Definitions
%% ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
% Initialize the in memory database by creating an empty list
init(in_mem) ->
{ok, []};
% Initialize the mnesia database by calling a setup function
% that is defined below
init(mnesia) ->
setup_mnesia_db(),
{ok, mnesia}.
%
% These gen_server function calls largely call functions defined below
% For that reason the functions they call are documented.
%
handle_call(get_nodes, _From, mnesia) ->
{reply, mnesia_get_nodes(), mnesia};
handle_call(get_nodes, _From, State) ->
{reply, list_nodes_internal(State), State};
handle_call({get_key, Node}, _From, mnesia) ->
{reply, mnesia_get_key(Node), mnesia};
handle_call({get_key, Node}, _From, State) ->
{reply, get_key_internal(Node, State), State}.
handle_cast({insert_key, Node, Key}, mnesia) ->
{noreply, mnesia_db_insert({Node, Key})};
handle_cast({insert_key, Node, Key}, State) ->
{noreply, insert_key_internal(Node, Key,State)}.
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
%% ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
%% Internal Function Definitions
%% ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
% search the in memory database for all of its nodes
list_nodes_internal(DB) ->
lists:map(fun({Name, _}) -> Name end, DB).
% Search for the key in the in memory database
% this also selects the first occurance of a node name
get_key_internal(Node, [ {Cur_node, Cur_key} | _Remainder_DB ])
when Node == Cur_node -> Cur_key;
get_key_internal(Node, [ {_Cur_node, _Cur_key} | Remainder_DB ]) ->
get_key_internal(Node, Remainder_DB);
get_key_internal(_Node, []) ->
[].
% put the new node and key pair on to the front of the database
insert_key_internal(Node, Key, DB) ->
[{Node, Key}|DB].
% Make sure that the mnesia database is up and running
% also make sure that a table with the correct schema exists
setup_mnesia_db() ->
mnesia:start(),
mnesia:create_table(keypair,
[{attributes, record_info(fields, keypair)}]).
% compose the provided node and key into a pair that conforms to the
% database schema. Then create a lambda function to insert the record
% into the database. Finally, we call that lambda function in a transaction
% so that it happens atomically.
mnesia_db_insert({Node, Key}) ->
Pair = #keypair{node = Node, key = Key},
Fun = fun() ->
mnesia:write(Pair) end,
mnesia:transaction(Fun),
mnesia.
% compose a function that
mnesia_get_key(Node) ->
F = fun() ->
Key = #keypair{node = Node, key = '$1'},
mnesia:select(keypair, [{Key, [], ['$1']}])
end,
{atomic, [Data]} = mnesia:transaction(F),
Data.
mnesia_get_nodes() ->
F = fun() ->
Node = #keypair{node = '$1', _ = '_'},
mnesia:select(keypair, [{Node, [], ['$1']}])
end,
{atomic, Data} = mnesia:transaction(F),
Data.