Supervision tree
Every long-lived process in Elixir and OTP lives under a supervisor, and Speechwave is no exception. Here’s the top level of the tree, straight from Speechwave.Application.
application.ex, :one_for_one
Speechwave.Application (supervisor) ├── Telemetry supervisor ├── Speechwave.Repo (Ecto / SQLite) ├── DNSCluster (multi-node discovery) ├── Phoenix.PubSub (name: Speechwave.PubSub) ├── Speechwave.RateLimiter (GenServer + ETS) ├── Speechwave.AuthThrottle (magic-link send throttling) ├── SpeechwaveWeb.Endpoint (HTTP + both WebSocket endpoints) ├── SpeechwaveWeb.Presence (Channel capacity tracking) └── Speechwave.DbBackup (only in production, once storage is configured)
The strategy is :one_for_one: if one child crashes, only that child restarts. That works here because there’s no shared state between children beyond PubSub, and PubSub is itself supervised.
Why :one_for_one is safe here
A one_for_one strategy assumes children are mostly independent. Speechwave's children fit that assumption well: RateLimiter, Presence, and DbBackup don't depend on each other, they each just do their own job and get restarted on their own if something goes wrong.
What happens when each child crashes
| Process | On crash | Data loss? |
|---|---|---|
| RateLimiter | Restarts, its ETS table is recreated empty | Cooldown state is lost; everyone effectively gets a fresh window. Acceptable by design. |
| Presence | Restarts, tracked presences clear | Participant count resets to zero until extensions reconnect, so capacity checks are briefly more permissive than usual. |
| DbBackup | Restarts, its timer resets | No database data is lost, a snapshot just doesn't get uploaded that cycle. See DB backup. |
| Repo | Connections are re-established | Any in-flight transaction is rolled back. |
AuthThrottle’s crash behavior isn’t covered here, since it hasn’t been specifically verified against the rest of this table.