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