Hello, everyone! I have been having a little bit of fun with processes and supervisors recently and I was thinking about how to better represent the/a world with processes…
I was imagining the following structure:
- One supervisor that watches over continents
- One supervisor per continent, that watches over its countries
- One supervisor per county, that watches over its states
- One supervisor per state, that watches over its cities
After my recently acquired knowledge about
DynamicSupervisors
, I think it’s probably the right way to go for the performance benefits.
Visually, I think it goes something like this:
[WorldSupervisor] ⟶ (NorthAmericaGenServer)
↳ [NorthAmericaSupervisor] ⟶ (UnitedStatesGenServer)
↳ [UnitedStatesSupervisor] ⟶ (VirginiaGenServer)
↳ [VirginiaSupervisor] ⟶ (RichmondGenServer)
[WorldSupervisor] ⟶ (SouthAmericaGenServer)
↳ [SouthAmericaSupervisor] ⟶ (BrazilGenServer)
↳ [BrazilSupervisor] ⟶ (SaoPauloGenServer)
↳ [SaoPauloSupervisor] ⟶ (SantosGenServer)
So, let’s imagine we want to start all of this dynamically. The first thing I tried was creating a WorldSupervisor
module that implements DynamicSupervisor
, to start all of it:
# ids can and will be more descriptive terms and names use a via tuple with a custom Registry
def start_continent(%Continent{id: id} = continent) do
name = name_for_process(id)
# starts a supervisor for the continent and starts the server under that supervisor (?)
{:ok, continent_sup} = DynamicSupervisor.start_child(__MODULE__, {__MODULE__, name: name})
{:ok, _server} = DynamicSupervisor.start_child(continent_sup, {ContinentServer, continet})
end
def start_country(%Country{id: id, continent_id: continent_id} = country) do
name = name_for_process(id)
# finds the continent that should supervise this country
continent_sup = find_continent_supervisor!(continent_id)
# starts a supervisor for the country and starts the server under that supervisor (?)
{:ok, country_sup} = DynamicSupervisor.start_child(continent_sup, name: name)
{:ok, _server} = DynamicSupervisor.start_child(country_sup, {CountrySever, country})
end
def start_state(%State{id: id, country_id: country_id} = state) do
name = name_for_process(id)
# finds the country that should supervise this state
country_sup = find_country_supervisor!(country_id)
# starts a supervisor for the state and starts the server under that supervisor (?)
{:ok, state_sup} = DynamicSupervisor.start_child(country_sup, name: name)
{:ok, _server} = DynamicSupervisor.start_child(state_sup, {StateSever, state})
end
def start_city(%City{id: id, state_id: state_id} = city) do
name = name_for_process(id)
# finds the state that should supervise this city
state_sup = find_state_supervisor!(state_id)
# starts a supervisor for the city and starts the server under that supervisor (?)
{:ok, city_sup} = DynamicSupervisor.start_child(state_sup, name: name)
{:ok, _server} = DynamicSupervisor.start_child(city_sup, {CitySever, city})
end
This is somewhat of a pseudo code. I wrote a version of this before, which didn’t work properly. I believe it was complaining about something related to starting the same process again (I think the supervisors were starting correctly but not the servers).
So, there are some problems I see with this implementation (besides the obvious ones):
- I’m starting to think that this is getting too complex for my taste. Would it be better to just have a “flat” organization? I mean, having
ContinentSupevevisor
,StateSupervisor
, andCitySupervisor
side by side. This way all continents, states, and cities are under just one supervisor. The obvious loss here is on the organization side since everything will be mixed under its own category of supervisor. - Given that starting all of this is dynamic, I’m using a custom Registry and I still haven’t found a way to show the proper names instead of its PIDs in
:observer
, which I think should work!?
So, has anyone tried something similar before? What do you think would be the better approach for a structure like this? Any comments are very welcome. Thanks in advance.