1
%% ``The contents of this file are subject to the Erlang Public License,
2
%% Version 1.1, (the "License"); you may not use this file except in
3
%% compliance with the License. You should have received a copy of the
4
%% Erlang Public License along with this software. If not, it can be
5
%% retrieved via the world wide web at http://www.erlang.org/.
7
%% Software distributed under the License is distributed on an "AS IS"
8
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
9
%% the License for the specific language governing rights and limitations
12
%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
13
%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
14
%% AB. All Rights Reserved.''
16
%% $Id: mnesia_registry.erl,v 1.2 2010/03/04 13:54:19 maria Exp $
18
-module(mnesia_registry).
20
%%%----------------------------------------------------------------------
21
%%% File : mnesia_registry.erl
22
%%% Purpose : Support dump and restore of a registry on a C-node
23
%%% This is an OTP internal module and is not public available.
25
%%% Example : Dump some hardcoded records into the Mnesia table Tab
27
%%% case rpc:call(Node, mnesia_registry, start_dump, [Tab, self()]) of
28
%%% Pid when pid(Pid) ->
29
%%% Pid ! {write, key1, key_size1, val_type1, val_size1, val1},
30
%%% Pid ! {delete, key3},
31
%%% Pid ! {write, key2, key_size2, val_type2, val_size2, val2},
32
%%% Pid ! {write, key4, key_size4, val_type4, val_size4, val4},
33
%%% Pid ! {commit, self()},
37
%%% {'EXIT', Pid, Reason} ->
40
%%% {badrpc, Reason} ->
44
%%% Example : Restore the corresponding Mnesia table Tab
46
%%% case rpc:call(Node, mnesia_registry, start_restore, [Tab, self()]) of
47
%%% {size, Pid, N, LargestKey, LargestVal} ->
48
%%% Pid ! {send_records, self()},
51
%%% {restore, KeySize, ValSize, ValType, Key, Val} ->
53
%%% {'EXIT', Pid, Reason} ->
57
%%% lists:map(Fun, lists:seq(1, N));
58
%%% {badrpc, Reason} ->
62
%%%----------------------------------------------------------------------
65
-export([start_dump/2, start_restore/2]).
66
-export([create_table/1, create_table/2]).
71
-record(state, {table, ops = [], link_to}).
73
-record(registry_entry, {key, key_size, val_type, val_size, val}).
75
-record(size, {pid = self(), n_values = 0, largest_key = 0, largest_val = 0}).
77
%%%----------------------------------------------------------------------
79
%%%----------------------------------------------------------------------
81
start(Type, Tab, LinkTo) ->
83
Args = [Type, Starter, LinkTo, Tab],
84
Pid = spawn_link(?MODULE, init, Args),
85
%% The receiver process may unlink the current process
89
{'EXIT', Pid, Reason} when LinkTo == Starter ->
93
%% Starts a receiver process and optionally creates a Mnesia table
94
%% with suitable default values. Returns the Pid of the receiver process
96
%% The receiver process accumulates Mnesia operations and performs
97
%% all operations or none at commit. The understood messages are:
99
%% {write, Key, KeySize, ValType, ValSize, Val} ->
100
%% accumulates mnesia:write({Tab, Key, KeySize, ValType, ValSize, Val})
103
%% accumulates mnesia:delete({Tab, Key}) (no reply)
104
%% {commit, ReplyTo} ->
105
%% commits all accumulated operations
106
%% and stops the process (replies {ok, Pid})
108
%% stops the process (no reply)
110
%% The receiver process is linked to the process with the process identifier
111
%% LinkTo. If some error occurs the receiver process will invoke exit(Reason)
112
%% and it is up to he LinkTo process to act properly when it receives an exit
115
start_dump(Tab, LinkTo) ->
116
start(dump, Tab, LinkTo).
118
%% Starts a sender process which sends restore messages back to the
119
%% LinkTo process. But first are some statistics about the table
120
%% determined and returned as a 5-tuple:
122
%% {size, SenderPid, N, LargestKeySize, LargestValSize}
124
%% where N is the number of records in the table. Then the sender process
125
%% waits for a 2-tuple message:
127
%% {send_records, ReplyTo}
129
%% At last N 6-tuple messages is sent to the ReplyTo process:
131
%% ReplyTo ! {restore, KeySize, ValSize, ValType, Key, Val}
133
%% If some error occurs the receiver process will invoke exit(Reason)
134
%% and it is up to he LinkTo process to act properly when it receives an
137
start_restore(Tab, LinkTo) ->
138
start(restore, Tab, LinkTo).
141
%% Optionally creates the Mnesia table Tab with suitable default values.
142
%% Returns ok or EXIT's
144
Storage = mnesia:table_info(schema, storage_type),
145
create_table(Tab, [{Storage, [node()]}]).
147
create_table(Tab, TabDef) ->
148
Attrs = record_info(fields, registry_entry),
149
case mnesia:create_table(Tab, [{attributes, Attrs} | TabDef]) of
152
{aborted, {already_exists, Tab}} ->
158
%%%----------------------------------------------------------------------
160
%%%----------------------------------------------------------------------
162
init(Type, Starter, LinkTo, Tab) ->
172
Starter ! {ok, self()},
173
dump_loop(#state{table = Tab, link_to = LinkTo});
175
restore_table(Tab, Starter, LinkTo)
178
%%%----------------------------------------------------------------------
180
%%%----------------------------------------------------------------------
186
{write, Key, KeySize, ValType, ValSize, Val} ->
187
RE = #registry_entry{key = Key,
192
dump_loop(S#state{ops = [{write, RE} | Ops]});
194
dump_loop(S#state{ops = [{delete, Key} | Ops]});
197
RecName = mnesia:table_info(Tab, record_name),
198
%% The Ops are in reverse order, but there is no need
199
%% for reversing the list of accumulated operations
200
case mnesia:transaction(fun handle_ops/3, [Tab, RecName, Ops]) of
202
ReplyTo ! {ok, self()},
203
stop(S#state.link_to);
205
exit({aborted, Reason})
208
stop(S#state.link_to);
210
exit({bad_message, BadMsg})
217
%% Grab a write lock for the entire table
218
%% and iterate over all accumulated operations
219
handle_ops(Tab, RecName, Ops) ->
220
mnesia:write_lock_table(Tab),
221
do_handle_ops(Tab, RecName, Ops).
223
do_handle_ops(Tab, RecName, [{write, RegEntry} | Ops]) ->
224
Record = setelement(1, RegEntry, RecName),
225
mnesia:write(Tab, Record, write),
226
do_handle_ops(Tab, RecName, Ops);
227
do_handle_ops(Tab, RecName, [{delete, Key} | Ops]) ->
228
mnesia:delete(Tab, Key, write),
229
do_handle_ops(Tab, RecName, Ops);
230
do_handle_ops(_Tab, _RecName, []) ->
233
%%%----------------------------------------------------------------------
235
%%%----------------------------------------------------------------------
237
restore_table(Tab, Starter, LinkTo) ->
238
Pat = mnesia:table_info(Tab, wild_pattern),
239
Fun = fun() -> mnesia:match_object(Tab, Pat, read) end,
240
case mnesia:transaction(Fun) of
241
{'atomic', AllRecords} ->
242
Size = calc_size(AllRecords, #size{}),
243
Starter ! {ok, Size},
245
{send_records, ReplyTo} ->
246
send_records(AllRecords, ReplyTo),
250
exit({bad_message, BadMsg})
256
calc_size([H | T], S) ->
257
KeySize = max(element(#registry_entry.key_size, H), S#size.largest_key),
258
ValSize = max(element(#registry_entry.val_size, H), S#size.largest_val),
259
N = S#size.n_values + 1,
260
calc_size(T, S#size{n_values = N, largest_key = KeySize, largest_val = ValSize});
261
calc_size([], Size) ->
264
max(New, Old) when New > Old -> New;
265
max(_New, Old) -> Old.
267
send_records([H | T], ReplyTo) ->
268
KeySize = element(#registry_entry.key_size, H),
269
ValSize = element(#registry_entry.val_size, H),
270
ValType = element(#registry_entry.val_type, H),
271
Key = element(#registry_entry.key, H),
272
Val = element(#registry_entry.val, H),
273
ReplyTo ! {restore, KeySize, ValSize, ValType, Key, Val},
274
send_records(T, ReplyTo);
275
send_records([], _ReplyTo) ->