In the previous lessons, you normalized failures using try/rescue, chained validations with with, and handled concurrent errors with Task. Now, you will take the next step toward resilience: letting the system recover automatically when a process crashes. In this lesson, you will create supervised GenServer workers that restart on failure and are reachable by name via a Registry.
Elixir’s Registry provides a way to associate names (like integers, strings, or tuples) with process PIDs. This allows you to look up a process by a stable name, even if its PID changes after a crash and restart.
- Looking up a process:
You can useRegistry.lookup/2to find the PID(s) registered under a given name: - Why use
viatuples?
The{:via, Registry, {RegistryName, key}}tuple lets you register and refer to a process by a logical name (like an id), not by its PID. This is important because when a process crashes and is restarted by a supervisor, it gets a new PID—but its name in the registry stays the same. This makes your system robust to restarts and avoids leaking atoms (as with:globalor:localnames).
What it does:
- Defines a
GenServerworker whose state is itsid. start_link/1registers the process name using aRegistryvia tuple, so you can look it up byid(not by PID).init/1prints when a worker starts.crash/1is a public API that sends an asynchronous message to the named worker.handle_cast/2raises an exception to simulate a failure; this will crash the process and let a supervisor restart it.
Notes:
- Naming via
Registryscales to dynamic workers and avoids global atoms. - Casting is fire-and-forget; here, it’s fine because we only trigger a crash.
- Exceptions in
GenServercallbacks terminate the process, which is exactly what a supervisor expects to handle.
What it does:
- Declares a
Supervisorthat starts threeWorkerprocesses with ids 1, 2, and 3. - Each
child_specuses a unique child id (:worker_1,:worker_2,:worker_3) to avoid id collisions. strategy: :one_for_onemeans if one worker crashes, only that worker is restarted.
Notes:
- Supervisors enforce a restart policy and intensity. By default, if more than 3 restarts (
max_restarts) occur within 5 seconds (max_seconds), the supervisor itself terminates. You can tune these viaSupervisoroptions when needed. - The default child restart setting for
GenServers is:permanent, which means they are always restarted if they terminate.
What it does:
- Starts a unique
RegistrynamedWorkerRegistryso each worker can be addressed by itsid. - Starts the
WorkerSupervisorand queries its children to see what’s running. IO.inspectprints the initial children list, including child ids and PIDs.Worker.crash(2)routes to the process registered with id 2, causing a crash.Process.sleep/1gives time to see “Worker 2 started” printed again as the supervisor restarts it.
You built a fault-tolerant setup: workers registered by id through Registry, supervised with a one_for_one strategy, and automatically restarted on failure. This complements the earlier lessons on error normalization and concurrent safety by adding system-level resilience.
Ready to solidify this skill? Head to the practice section to apply supervisors, process naming, and restarts in realistic scenarios.
