gen_leader's status.
Scott Lystig Fritchie
fritchie@REDACTED
Tue Aug 15 22:17:21 CEST 2006
>>>>> "aa" == Alex Arnon <alex.arnon@REDACTED> writes:
aa> - I need a group of processes (1 per node) of which only
aa> one periodically wakes up and performs cleanup on some mnesia
aa> tables.
aa> - It is probably not too bad if occasionally (due to node
aa> crashes etc.) more than one would wake up, as I believe it should
aa> be simple to avoid if nodes are reasonably stable.
Alex: I've done the same thing for almost exactly the same reason:
avoid running Mnesia clean-up stuff on more than 1 node. The clean-up
functions are reasonable enough so that if more than 1 node runs them
at the same time, it's an inconvenient use of CPU cycles, but no harm
is done.
The top-level supervisor has (yet another) worker process that takes
care of this task. It periodically polls using global:whereis_name/1,
and if the well-known name ('highlander')(*) isn't registered, it
tries using global:register_name/2. If it succeeds, then subsequent
gen_server calls to its query function will return true.
The gen_server init/1 callback uses timer:send_interval/2 to send a
{try_to_register} tuple periodically to the local highlander server.
Whenever a call to the_one_p predicate will use global:whereis_name/1,
just in case something evil/weird/unpredictable happened since
global:register_name/2 was called. Again, if 'global' doesn't do the
right thing 100% of the time, no harm done.(**)
-Scott
(*) "There can be only one!" -- http://www.imdb.com/title/tt0091203/
(**) I don't know exactly what 'global' does in cases of network
partition, but I guess that it will register the global name on each
side of the partition. (?)
-record(state, {
name, % Global name to register
timerref, % For cancelling timer
the_one = no % Am I the one?
}).
handle_call({the_one_p, Name}, _From, State)
when State#state.the_one == yes, Name == State#state.name ->
Me = self(),
case global:whereis_name(State#state.name) of
Me ->
{reply, true, State};
_ ->
%% Oh oh, someone did something dastardly like calling
%% global:unregister_name/1. We aren't The One any longer,
%% Give up.
{stop, lost_master_role, false, State}
end;
handle_call({the_one_p, _Name}, _From, State) ->
%% All other cases: no, we aren't The One.
{reply, false, State};
...
handle_info({try_to_register}, State) when State#state.the_one == yes ->
Me = self(),
case global:whereis_name(State#state.name) of
Me ->
{noreply, State};
_ ->
%% Someone has done something very, very bad. Give up.
{stop, lost_master_role, State}
end;
handle_info({try_to_register}, State) when State#state.the_one == no ->
case global:register_name(State#state.name, self()) of
yes ->
{noreply, State#state{the_one = yes}};
no ->
{noreply, State}
end;
...
More information about the erlang-questions
mailing list