sschuldenzucker

sschuldenzucker

Supervision design for static but interdependent processes, startup order

Hey all, I’m pulling my hair out over how to properly design my supervision structure. It works right now but only because processes are restarted often enough to eventually fall in the right order.

I’m sure there’s an obvious right solution here that I just don’t see (pretty new to elixir). Anyone got an idea? :folded_hands:

I’m building a single-user interactive app. There are four GenServers:

  • The ModelManager serves as a central point to store the underlying data. (makes it easier to implement sync later) Processes can subscribe to the ModelManager and be notified in the event of changes.
  • The Frontend (a TUI using the Ratatouille library) presents the UI and pushes changes to the ModelManager. It also listens to change events emitted from the ModelManager (e.g., because of sync).
  • The frontend actually needs a little MsgTransformer process between itself and the ModelManager to transform the messages into a shape that Ratatouille can deal with.
  • The FileSaver implements autosave by listening to change events from the ModelManager and writing them to disk.

I even made a little diagram:

The problem I’m facing is startup: in principle it’s clear what should happen:

  • The ModelManager should start first.
  • Then we can start FileServer and MsgTransformer (the MsgTransformer actually belonging to the Frontend; nobody else has to know about this)
  • Finally we can start the Frontend.

This order is necessary so everyone can subscribe / reference the other processes when they need to. But I have no idea how to make this happen.

Right now I’m just putting all four processes as directly supervised children under my main app tree. Some process (the frontend specifically) crash upon init because the ModelManager isn’t live yet, so it can’t subscribe. But the supervisor restarts it, so it’ll eventually be fine. — But this is definitely not a clean design right?

Most Liked

axelson

axelson

Scenic Core Team

It sounds like you’re pretty well sorted on the questions you had, but reading your post I think that I’d recommend not having a separate process for the MsgTransformer, and instead convert that into a plain module that is used by the frontend process. My reasoning is that it sounds like the MsgTransformer process is being used for code organization rather than isolation/resilience (plus adding a process always adds performance overhead). If you haven’t already, I highly recommend reading Saša Jurić excellent blog post on this topic: The Erlangelist - To spawn, or not to spawn?

cmo

cmo

Children are started in the order you list them. Once one returns from init, the next one starts.

w0rd-driven

w0rd-driven

If Frontend depends on it then it should start first. Your second point kind of illustrates that. You could try to detect if the named process is started but you might as well use supervisors to guarantee it.

You are basically pulling the childspec calls from your app into each supervisor with a bit more boilerplate. A supervisor is a behavior of a GenServer, which I map to something like traits in PHP. Since you want to manage dependent processes, it makes sense to group them as a supervisor tree and when your dependency crashes you likely want to re-initialize some state for it.

I think you understand quite a bit but it helps to put theory to practice. You could spike on a throwaway branch and try different strategies or groupings until it “feels right”. You could also look at all the other children your app handles. The docs for supervisors Supervisor — Elixir v1.17.2 has examples and is pretty comprehensive. The DockYard Academy section on supervisors and the drills are a great as well curriculum/reading/supervisors.livemd at main · DockYard-Academy/curriculum · GitHub.

w0rd-driven

w0rd-driven

I typically group processes by strategy and dependencies. If you need to start a collection of processes together then I would place them under a dedicated supervisor. You may want the :one_for_all so that a restart in one restarts all of them. You can use module based supervisors and either split the responsibility or just tack it on. I would personally only split when the friction required it.

You’ve described your supervision tree perfectly here. The app starts only ModelManager, that supervises FileServer and Frontend where frontend is also a supervisor for MsgTransformer.

sschuldenzucker

sschuldenzucker

Hey, thanks for this! Can I ask a few follow-up questions?

  • I’m kinda hesitant to have ModelManager supervise the frontend (for instance) because these are not really logically connected: It’s thinkable that a variant of the app would run without the frontend (and instead with a web frontend or GUI, for instance, or as a sync server). Any alternatives come up here?
  • How do I make sure the ModelManager is started before the frontend etc.? As in, what code do I use? I feel a bit dumb here, honestly, as if there’s some command I just don’t know?
  • Oh, and can something be both a GenServer and a Supervisor at the same time? (possible I still have my OOP goggles on a bit too tightly here, maybe it’s just fine?)

Where Next?

Popular in Questions Top

Tee
can someone please explain to me how Enum.reduce works with maps
New
LegitStack
I’m hoping you guys can give me some general advice and perhaps code examples if you’re feeling up to it. I’m very interested in Elixir,...
New
albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New
_russellb
I want to try my hand at web scraping. What tools/libraries do I need to use. I’m hoping to turn this into something professional so don’...
New
Jim
As a follow up to my earlier question: I have the code compiling and running but not getting a successful login from the rest server. ...
New
Kagamiiiii
Student & New to elixir. Nice language. I want to convert a english character, e.g. “a”, which is stored in a variable, to it’s asci...
New
vac
Hi, I'm quite new in Elixir and I'm trying to format a string to a PEM format. I have the certificate value like MIIDBTCCAe2...... and ...
New
gonzofish
I’m currently trying to understand how to join three tables using Ecto. All the examples I’ve seen use 2, so maybe I’m just missing somet...
New
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
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

JakeBecker
TL;DR: I’ve just released an implementation of Microsoft’s IDE-independent Language Server Protocol for Elixir. It adds language support ...
1140 51847 244
New
albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New
New
polypush135
As many of you may have realized by now (sorry for all the posts here) I’ve been working on a db problem where I’m trying to aggregate a ...
New
lk-geimfari
What is most correct way to open, read and parse JSON file with poison? For example if we have example.json file in root of some projec...
New
lastday4you
I wanted to check elixir version in phoenix because i found that my elixir is 1.5 but when i use Enum.chunk_by it said the function is un...
New
myronmarston
The Elixir Typespec docs show the following syntax for keyword lists in typespecs: # ... | [key: type] # keyword lis...
New
mgjohns61585
Could someone help me? I'm making my first elixir program, number guessing game. I can't figure out how to convert the user's guess from ...
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
AstonJ
We’ve put together this wiki for Phoenix LiveView - please feel free to add any info you feel is worth including. What is Phoenix LiveV...
New

We're in Beta

About us Mission Statement