darkmarmot
I have many actor processes in my application with event counts associated with each actor.
I’d like to have a live sum of the total actor events across the node (basically map-reduce across the processes).
I was thinking that I could register each process in the Registry with a count – and store a separate sum in the Registry that gets incremented with new events and decremented when actors crash (using trap_exit?)
Does that sound like an appropriate solution in Elixir or is there a more canonical OTP way to handle this?
Thanks,
Scott S.
Trending in Questions
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
Hello,
I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
Hi everyone,
I am toying with the idea of building a “match maker” for giving personal help to people that wants to start coding.
I sta...
New
Documentation
While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
So my question is quite simple and i have found no conclusive answer on forum, google or AI.
Should we use :erlang.float for Integer to ...
New
I recently noticed that Elixir’s Logger defaults its primary log level to :debug when no :logger, :level application configuration is pre...
New
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
Other Trending Topics
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
I am happy to introduce the very α version of the new programming language compiled to BEAM.
Welcome Cure.
It has literally three kille...
New
Hi there! We created Gust: A task orchestrator inspired by Airflow.
For those who have never heard about Aiflow, it’s a Python-based wor...
New
Hi everyone!
The first release candidate for the Expert language server project is now available!
We’ve published a press release detai...
New
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixirconf-us
- #ai
- #blog-post
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #metaprogramming
- #hex
- #security










Showing Posts 1 to 10- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
christhekeele
Hey Scott! I’d probably use ETS for this, that should get you started down a more idiomatic path.
dimitarvp
ETS has a function for atomically increasing a single number variable.
Called from Elixir like this:
:ets.update_counter.darkmarmot
Thanks, update_counter looks good!
Should I make a genserver that monitors every actor to decrement counts when they crash?
Or is there a better way for handing that part?
christhekeele
Depending on if your workers implement OTP behaviours, they should have terminate call backs you could decrement from.
cmkarlsson
With the warning that the
terminatecallback is not always called and cannot be relied upon to run.sasajuric
+1 that
terminatecallback is not the appropriate place to cleanup counters of processes which are terminated. You need another GenServer to monitor these processes and perform the cleanup.Instead of rolling your own, you could use gproc aggregated counters.
To make that work, you need to register the aggregated counter, e.g. in your application start callback, or in some top-level process:
Now, in every process, you initialize the local counter when the process starts:
Where
0is the initial count for that process.When you want to change the counter value, you can use
update_counter:The
:cand:lindicate that you’re updating a local counter which is tied to the current process. If the process terminates, its count will be removed from the aggregated count.To get the aggregate value (sum of all counters), you need to invoke:
Demo:
amnu3387
But is there any way you can decide if using
terminate’s callback is appropriate or not? Like, if it’s a clean exit, it will always be called, if it’s a crash it might or might not, has it to do if you’re using distributed erlang, what are the guidelines to decide if usingterminateis an appropriate decision or not?For instance, I have a genserver responsible for processing files, assuming it doesn’t crash, is it ok to, for instance, remove the file from the
terminatecallback?It just seems that if you can “never” rely on it, under any circumstances that it doesn’t make sense to even exist? Or am I missing something about its implementation and use cases?
sasajuric
This is a very good question. I personally mostly avoid terminate, because it won’t be invoked if the process crashes, or if it’s forcefully terminated (killed) from the outside. Thus, if some cleanup code must be executed, I prefer having another process to do it.
However, using a cleanup process isn’t synchronous, since the cleanup code will run after the “main” process has terminated. Therefore, there are some special cases where
terminateworks better. For example, supervisor terminates children from the terminate callback. This ensures that when the supervisor goes down, its complete subtree is already down. I can’t think of a way to ensure such synchronism by using a separate cleanup process.However, this approach suffers from potential theoretical issues. If a supervisor process is brutally killed or if it crashes, then this guarantee doesn’t hold. The child processes will still be taken down eventually, but not immediately. If some descendant is trapping exits and ends up in an infinite loop, it might never happen. This in turn could prevent the restart of the crashed supervisor, which could take down the entire system, or it could lead to duplicate processes running, which could cause some strange behaviour of the system.
However, we can assume that the supervisor process is thoroughly tested and hopefully free from unexpected crashes. In addition, in a properly constructed supervision tree, a supervisor is never brutally killed (because
:shutdownof a supervisor is:infinityby default), so I’d say that these issues are theoretical, and very unlikely to occur in practice.So my take would be to use
terminateto implement a synchronous cleanup (things are cleaned up before the process terminates). For example, I use it in Parent to terminate children, similarly to supervisor. In such cases, you probably want to keep the logic of the process simple, to reduce the chance of it crashing. You may also consider setting it’s shutdown option to:infinityto prevent its parent from brutally killing it.If the synchronism is not required, and a cleanup code must be executed when a process goes down, regardless of how/why it goes down, I’d suggest using a separate process.
amnu3387
Thank you @sasajuric
So the takeaway is that usually creating the process (ie. genserver) and monitoring it from another process (ie. another genserver) is a more sturdy solution for effectively dealing with cleanups?
And the monitor would be part (usually) of the application root supervision since it would only deal with monitoring/cleanup, while the processes executing “work” would be part of their own subtree/supervisor?
I might be wrong, but I had the idea that while developing some genservers in the past that held “game state”, sometimes I would crash them while working on the code, and yet the terminate was still called?
sasajuric
Yes, it’s a bit more nuanced. A crash in
init/1won’t lead toterminatebeing invoked, while an exception raised byhandle_*will. However, a linked error (e.g. a child task crashes) won’t lead toterminate, because the exit signal will take the process down (unless it’s trapping exits). Moreover, if a parent supervisor decides to stop the server,terminateis invoked only if the server is trapping exits.As you can see, there are all sorts of edge cases here, but the main point is that, no matter what you do, you can’t be completely sure that the termination logic is invoked, so if you want stronger guarantees, it’s IMO better to use a separate process. Of course, not even that will ensure that the cleanup code is invoked, e.g. if BEAM OS process is killed, or someone pulls the power plug
, but within the BEAM instance, you have more guarantees than when using
terminate.