Refefer
Refefer

Reputation: 551

Transient gen_server processes and updating pids

I'm currently learning Erlang at a reasonable clip but have a question about gen_server with supervisors. If a gen_server process crashes and is consequentially restarted by a supervisor, it receives a new pid. Now, what if I want other processes to refer to that process by Pid? What are some good idiomatic ways to 'update' the Pid in those processes?

As an exercise with some practical application, I'm writing a lock server where a client can request a lock with an arbitrary key. I ideally would like to have a separate processes handle the locking and releasing of a particular lock, the idea being that I can use the timeout argument in gen_server to terminate the process if no one has requested it after N amount time, so that only currently relevant locks will stay in memory. Now, I have a directory process which maps the lock name to the lock process. When the lock process terminates, it deletes the lock from the directory.

My concern is how to handle the case where a client requests a lock while the lock process is in the middle of terminating. It hasn't shutdown yet, so sniffing that the pid is alive won't work. The lock process hasn't reached the clause that deletes it from the directory yet.

Is there a better way to handle this?

EDIT

There are two gen_servers currently: the 'directory' which maintains an ETS table from LockName -> Lock Process, and the 'lock servers' which are added dynamically to the supervision tree using start_child. Ideally I would like each lock server to handle talking with the clients directly, but am worried about the scenario of a request to acquire/release getting issued with call or cast when the process is in the middle of crashing (and thus won't respond to the message).

Starting with {local} or {global} won't work since there can be N amount of them.

Upvotes: 2

Views: 472

Answers (3)

arun_suresh
arun_suresh

Reputation: 2925

You can completely avoid the use of your "lock_server" process by using the "erlang:monitor/demonitor" API.

When a client requests a lock, you issue the lock.. and do a erlang:monitor on the client.. This will return you a Monitor Reference.. You can then store this Reference along with your lock.. The beauty of this is that your directory server WILL be notified when the client dies.. you could implement the TIMEOUT thing in the client.

Here is a snippet from code I had written recently.. https://github.com/xslogic/phoebus/blob/master/src/table_manager.erl

Basically, the table_manager is a process that issues lock on a particular table resource to client.. if the client dies, the table is returned to the pool..

Upvotes: 1

I GIVE CRAP ANSWERS
I GIVE CRAP ANSWERS

Reputation: 18879

The trick is to name the process and don't refer to it by its pid. You generally have 3 viable options,

  • Use registered names. This is what andreypopp suggests. You refer to the server by its registered name. locally registered names have to be atoms, which may somewhat limit you. globally registered names do not have this limitation, you can register any term.

  • The Supervisor knows the Pid. Ask it. You will have to pass the Supervisor Pid to the process.

  • Alternatively, use the gproc application (exists on http://github.com). It allows you to create a generic process registry - you could have done that by ETS, but steal good code rather than implement yourself.

The pid is usable if all processes are part of the same supervision tree. So the death of one of them means the death of the others. Thus, the Pids recycling doesn't matter.

Upvotes: 5

andreypopp
andreypopp

Reputation: 6951

Don't refer to gen_server process by pid.

You should provide API for your gen_server via gen_server:call/2 or gen_server:call/3 functions. They are accept ServerRef as first argument, which can be Name | {Name,Node} | {global,GlobalName} | pid(). So, you API would look like:

lock(Key) ->
  gen_server:call(?MODULE, {lock, Key}).
release(Key) ->
  gen_server:call(?MODULE, {release, Key}).

Note that this API is defined in the same module as your gen_server and I assume you starting you server with something like:

gen_server:start_link({local, ?MODULE}, ?MODULE, [], [])

So your API methods can lookup server not by pid, but by server name, which is equal to ?MODULE.

For more information, please see gen_server documentation.

Upvotes: 4

Related Questions