sheharyarn
I writing an Elixir app with GenServer that starts an external application on boot and shuts it down and does other clean-up on exit. I’ve added bootup functionality in the init/1 callback and cleanup code in the terminate/2 callback.
The init code works fine when the GenServer is started, and the terminate method is also called when the :stop signal is manually sent, but in the cases of unexpected shutdowns and interrupts (as in the case of hitting Ctrl+C) in IEx, the terminate code is not called.
What’s the proper way of doing cleanup when my Elixir app crashes or is unexpectedly shutdown?
Here’s the parallel StackOverflow Question and my code:
defmodule MyAwesomeApp do
use GenServer
def start do
GenServer.start_link(__MODULE__, nil)
end
def init(state) do
# Do Bootup stuff
IO.puts "Starting: #{inspect(state)}"
{:ok, state}
end
def terminate(reason, state) do
# Do Shutdown Stuff
IO.puts "Going Down: #{inspect(state)}"
:normal
end
end
MyAwesomeApp.start
Trending in Questions
Hello!
Suppose you are building workflow (order / task / payment) processing system with the following requirements:
Each workflow con...
New
I’m in search of an Elixir library that offers PDF generation capabilities similar to Ruby’s Prawn. While there have been discussions abo...
New
I’m looking to build a personal workflow to quickly deploy web applications written in elixir/phoenix, for local consumption (ie not on t...
New
Before I dive in myself, did anyone successfully sprinkle Hologram into their existing LiveView app?
Looking for hints regarding:
Addi...
New
Kia ora,
We have been using elixir-google-api to connect to Google Drive. However, with the updates to Tesla due to CVEs this is now bro...
New
Hi all, I wanted to ask how the community is dealing with post-release steps.
Today we have Ecto migrations, which make sure that the db...
New
Hello,
I have an Elixir backend that implements a custom protocol over TCP. I want to load test the backend and assess the performance 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
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve.
They are GUI (Emerge) and State management (S...
New
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
Emily is an Elixir library that runs Nx computations on Apple’s MLX. Install it as the default Nx backend and Nx, defn, Axon, Nx.Serving,...
New
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #blog-post
- #phoenix_html
- #iex
- #graphql
- #ai
- #genstage
- #elixirconf-us
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #metaprogramming
- #security
- #hex










Showing Posts 1 to 10- Show Best Posts
- Show All Posts (oldest first)
- Show All Posts (newest first)
OvermindDL1
The ‘proper’ way to shut down a node from the console (or in code) would be
:init.stop(), which can take an optional integer that is the return code of the program. Ctrl+c is a harsh stop (I wish iex would bind it to:init.stop()with, say, a 30 second timeout that counts down on the screen, maybe displaying what it is waiting on to top too).sheharyarn
So there’s currently no way to catch Ctrl+C (and other sudden) exits?
sasajuric
To increase chances of the
terminatecallback being invoked, the server process should trap exits. However, even with that, the callback might not be invoked in some situations (e.g. when the process is brutally killed, or when it crashes itself). For more details see here.As mentioned, if you want to politely shutdown your system, you should invoke
:init.stop, which will recursively shutdown the supervision tree causingterminatecallbacks to be invoked.As you noticed, there is no way of catching abrupt BEAM OS process exits from within. It’s a self-defining property: the BEAM process terminates suddenly, so it can’t run any code (since it terminated)
Hence, if BEAM is brutally terminated, the callback will not be invoked.
If you unconditionally want to do something when BEAM dies, you need to detect this from another OS process. I’m not sure what’s your exact use case, but assuming you have some strong needs for this, then running another BEAM node, on the same (or another) machine, could work here. Then you could have one process on one node monitoring another process on another node, so you can react even if BEAM is brutally killed.
However, your life will be simpler if you don’t need to unconditionally run some cleanup logic, so consider whether the code in
terminateis a must, or rather a nice-to-have?sheharyarn
That’s a pretty detailed answer that covers most of my concerns. Would you consider also posting this as an answer on my StackOverflow Question - for future reference?
sasajuric
Sure, I copy-pasted it there.
pdawczak
That’s absolutely great answer - thanks @sasajuric. It confirms this unfortunate situation - what approach would you suggest then for handling
ports closure upon killingiex?I’m opening
portto boot executable up when elixir application is started (https://ngrok.com/ to be more precise). This is used for development purposes, so surely, the way you use this dev-env is, if you need to reboot the app - you’re killing it (Ctrl-C) andiex -S mixagain, but this crashes as the executable has not been killed along the way.The way I was trying to handle that, was to send
kill -9to process fromterminate, but this is not invoked…Thanks for your advice!
pma
If the Erlang VM stops, the external port process will receive a signal indicating that the stdin closed.
If ngrok doesn’t gracefully stop on CTRL+D, you can use a small bash wrapper to handle the stdin closing and then killing ngrok with the appropriate signal.
pdawczak
Thanks @pma!
Yes, indeed -
ngrok(when watching:watch -n 1 "ps ax | grep ngrok") seems to disappear after some delay after killingiex. Unfortunately, it is not instantaneous and it’s indeterministic - it’s alive from couple sec to up to 30 sec, and just then disappears.This is a problem, because if you’re trying to kill (eg. Phoenix dev server) and start again shortly after - everything crashes, as new
ngrokcan’t be opened (unless the previous one dies quickly enough before attempting to open a fresh one).I also tried a little wrapper script (like the one described here). This, in fact,
killsngrokalong exitingiex, but it suffers another problem - whenngrokcrashes (for whatever reason, I’ve tried to mimic that by killing -kill -9thengrokprocess), this doesn’t propagate up viaport, as thepidof running process points to the wrapping script.sasajuric
This is the correct hint. The external process will get EOF on its stdin. However, if the external program is busy doing something else, it could linger for much longer before it detects that. I’ve written a bit about this here (see “Program Termination” section).
IMO, the cleanest solution, if you own the code of the external program, is to adapt it to run processing in a separate thread, while the main thread just does I/O. That way, the external program can immediately detect the termination of the other side, and terminate itself immediately.
If that’s not an option, I think (but not sure) that Porcelain by @alco might offer some automagical help.
pdawczak
Thank you @sasajuric, this is very valuable answer!
I’ve read through your article earlier, when I was starting playing with
ports and it brought a lot covering basics. Great post!You’ve stated:
This is the bit I wasn’t sure - because
iexsession is killed (this is what happens withCtrl-C+Ctrl-C, right?) does Elixir have time to sendEOF?Unfortunately, ngrok is application I don’t own and I had suspicion what happens, but you’ve brought final confirmation. As mentioned earlier, wrapping with script was causing problem of not propagating it stopped and as such, it was difficult (impossible?) to detect it and supervise - that was the reasoning I’ve decided to go without wrapping script.
Thank you for your input! I truly appreciate it!