DaAnalyst

DaAnalyst

GenServer timeout

I use GenServer (restart: :temporary) for a module that manages some state and handles access to it. Its init function and its handle_* functions return the same timeout value. The GenServer-based state-managing processes get started by invoking GenServer.start_link and a :via tuple for the name.

I also have another module that uses DynamicSupervisor to supervise the aforementioned processes and a function to start them as its children.
When a process with a certain name already exists (for it has not timed out at the time of fetching its pid), the code pattern-matches GenServer.start_link return value with { :error, { :already_started, pid}} and returns the pid of the process that is still alive. If the process does not exist, the DynamicSupervisor starts a new one and returns its pid.
This all works perfectly fine.

Now, the question part. When the client calls this pid fetching function which calls GenServer.start_link which in turn constructs the process name and fetches the pid (of an existing processes or a newly started one), the next line in the client code uses this pid for invoking a business function (GenServer.call) that ends up modifying the managed state. Since an existing process can have an arbitrary time left until the timeout, it is possible that it times out precisely between those two lines of code (an extremely rare situation, but it may happen) in which case the call will exit with:

** (EXIT) no process: the process is not alive or there’s no process currently associated with the given name, possibly because its application isn’t started.

How should I deal with this probability in general? Should I trap this exit? What is the best practice to handle this?

Thanks

Marked As Solved

lud

lud

I mean this problem is so obvious

I agree but the problem exists because we use a Registry for our processes. I think that there is no way to ensure that your process cannot stop in between a fetch on the registry and a call ot the process.
I believe you can only mitigate the problem and that people either just go optimistic (ignore the problem because the timeframe is soooo small that it virtually never happens) or just try/catch a no proc error.

Possible solutions

You could create a fetch_pid that executes a lookup in the registry and then ask (call) the process if it is alive (which will refresh the timeout), and finally return the pid. Not very satisfying and adds more load on the system.

If you do not have many users yet you could say it is premature optimisation and just reload your data. Or use an ETS cache without concurrency so the data either exists or not, period. I personally use a queue for that instead of a dedicated process for each entity, but the data is copied back and forth between ETS and the worker.

Another solution which is technically correct but heavy is to use a synchronous registry, and have the registry handle stopping your temporary processes : on timeout, the temporary process sets a flag idle: true, and tells the registry “i am idle” (with a cast). Then the registry will call the process, asking if “still idle ?” and if yes, terminate it. But it is so heavy I would not do that.

Use a pool : instead of temporary processes that handle each entity, have N workers that handle multiplie entities, hash your entity to a worker with erlang:phash2/1 and have the worker manage the cleanup of memory for each entity of its own.

I also have an elixir library called mutex. There is a feature that I would add to it : when a process locks a key that was previously locked, the new process can inherit data from the previous owner. I can add the feature if you want. So you lock the key, inherit the data (or load it from database if it expired), do your work, and release the key with the new data. But again your 100K of data will be copied multiple times in memory.

In the end, a try/catch is the simple thing to do.

Also Liked

lud

lud

Glad that you found a new home !

Also I forgot to tell, but if the code that calls your registry and then your worker is supervised, and could be restarted issuing the same command, you could also just let the no-process error crash :slight_smile:

lud

lud

I feel it is the same : try/cath and retry is the same as crashing the process with noproc and have it be restarted. In both cases your code will fetch the pid again and call your worker (all at once using :via).

The latter will repeat more code obviously, but very rarely, and keep the codebase clean.

If you are for example in a controller, you’d rather try/catch because the process will not be restarted and just send a 500 back. But if you are for example in a queue worker, the worker would be restarted on failure and there I’d let it crash. Depends on where you are :slight_smile:

Where Next?

Popular in Questions Top

sergio
In Ruby, I can go: User.find_by(email: "foobar@email.com").update(email: "hello@email.com") How can I do something similar in Elixir? ...
New
vonH
In asking this question I am more interested about the expressiveness of the language itself and less concerned about the availability of...
New
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New
pgiesin
This should be a simple problem but I just can’t seem to figure it out. I have a standalone Elixir app that won’t find the database. Dep...
New
New
myronmarston
The Elixir Typespec docs show the following syntax for keyword lists in typespecs: # ... | [key: type] # keyword lis...
New
rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list....
New
kostonstyle
Hi all I want to have a unix time, from the current time plus 1 hour. DateTime.now + 1 hour How to get it in elixir? Thanks
New
Qqwy
Original source of discussion: This topic on the Pragmatic Programmers' Functional Web Development with Elixir, OTP, and Phoenix forum. ...
New
jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
New

Other popular topics Top

Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New
AstonJ
You’re a programmer, so you don’t need spoon feeding with the conventional drivel about “this is an integer.” No. You need to know what’s...
New
malloryerik
Hi, this is for people who, like me, have had some friction using .html.heex templates in VSCode. The solution seems to be, in a hyphena...
New
chrismccord
This release brings a number of exciting features, including integration with the new Phoenix LiveDashboard and Phoenix LiveView. There h...
New
chrismccord
As promised, the first release candidate of Phoenix 1.3.0 is out! This release focuses on code generators with improved project structure...
New
myronmarston
The Elixir Typespec docs show the following syntax for keyword lists in typespecs: # ... | [key: type] # keyword lis...
New
qwerescape
Is there a way to get the call stack or stack trace at any point in the code? Not from exceptions, but an expression that returns how the...
New
johnnyicon
Hi all, I've just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I'm trying to use Postg...
New
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New

We're in Beta

About us Mission Statement