asobi_extension_sup (asobi v0.84.0)
View SourceOne supervisor per extension, under one supervisor for the lot.
asobi_sup (one_for_one, 10/60)
|- ...core children...
`- asobi_extension_sup (one_for_one, 3/60)
|- quests (transient, own intensity)
|- clans (transient, own intensity)
`- asobi_extension_watchThe reason sup/0 is a contract key at all, rather than extensions being
plain OTP applications with their own mod, is that applications in a release
are permanent by default, and in OTP a permanent application terminating
takes the whole runtime with it. An extension supervising itself and
exceeding its restart intensity therefore kills matchmaking, presence and
every live match. This is the ordinary BEAM pattern rather than an invention:
Ecto repos, Oban and Phoenix endpoints are all started in the host's tree.
Three deliberate choices:
one_for_oneat both levels. Extensions are independent by construction: they share onlyasobi_repo, and the wire seam between them is RPC. One restarting is never a reason to restart another.- Each extension's sub-supervisor is
transient. Exceeding a restart intensity makes a supervisor exit with reasonshutdown, which a transient child is not restarted for. So an extension that has burned its own budget goes dark and stays dark, attributed by name, instead of escalating. Restarting it here would only relay the same crash loop upwards until it reachedasobi_supand took the node with it, which is the exact outcome this tree exists to prevent. Transient rather than temporary because a sub-supervisor killed for any other abnormal reason is a core-side fault, not an extension giving up, and should be restarted. - Intensity 3 in 60 here. Extension failures never consume it: a
transient child exiting
shutdownis not a restart. The budget coversasobi_extension_watchonly, so nothing an extension does can exhaust it.
Per-extension intensity defaults to 5 in 60 and is settable with
{extension_restart, #{intensity => I, period => P}} in asobi's app env.
Readiness is a precondition, not a race
An extension child that queries at init/1 needs the pool up and its own
tables created, and must not crash if they are not - a crash loop here ends
with the extension dark and staying dark, which is exactly the outcome the
transient sub-supervisor is for. Rather than making every extension carry a
retry path, a loaded-marker and a fallback, core makes the precondition hold.
asobi_app:start/2 runs kura_migrator:migrate/1 and then
asobi_readiness:mark_ready/0 before asobi_sup:start_link/0, so by the
time this supervisor initialises the answer is already final and cannot flip
later. If it is false, migrations did not complete: no extension starts and
one line says which ones. asobi_readiness:guard/0 is the other half of the
same marker, and answers 503 on the dispatch path for the same reason.
The extension is dark either way. The difference is that it is dark for a
stated reason instead of after burning its restart budget, and that init/1
gets to assume a working database.
Summary
Functions
The extensions whose sub-supervisor is alive. An installed name missing here has gone dark.
Functions
-spec init([]) -> {ok, {supervisor:sup_flags(), [supervisor:child_spec()]}}.
-spec running() -> [asobi_extension:name()].
The extensions whose sub-supervisor is alive. An installed name missing here has gone dark.
-spec start_link() -> supervisor:startlink_ret().