Supervision
A child service crashes. Detect it, restart it, and stop restarting if it keeps failing. The foundation of fault-tolerant systems.
Hew
Supervisors are a language construct. The strategy and the restart budget (intensity: N within <window>) are supervisor fields; each child picks a restart: policy. permanent means always restart, transient means restart only on error. intensity: 5 within 60s caps restarts to 5 within 60 seconds before the supervisor gives up.
actor DatabasePool {
var pool_size: i64 = 0;
receive fn query(sql: string) -> string { "" }
}
actor CacheLayer {
var capacity: i64 = 0;
receive fn get(key: string) -> string { "" }
}
actor ApiHandler {
var port: i64 = 0;
receive fn handle(req: string) -> string { "" }
}
supervisor ServiceCluster {
strategy: one_for_one;
intensity: 5 within 60s;
child db: DatabasePool { pool_size: 16 } restart: permanent;
child cache: CacheLayer { capacity: 1024 } restart: permanent;
child api: ApiHandler { port: 8080 } restart: transient;
}Erlang/OTP
Erlang/OTP is the supervision model that Hew borrows from. The same concepts exist — one_for_one, intensity/period, permanent/transient — but expressed through OTP behaviour callbacks, maps, and tuples rather than language-level declarations.
-module(service_cluster_sup).
-behaviour(supervisor).
-export([start_link/0, init/1]).
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
init([]) ->
SupFlags = #{strategy => one_for_one,
intensity => 5,
period => 60},
Children = [
#{id => db,
start => {database_pool, start_link, []},
restart => permanent,
type => worker},
#{id => cache,
start => {cache_layer, start_link, []},
restart => permanent,
type => worker},
#{id => api,
start => {api_handler, start_link, []},
restart => transient,
type => worker}
],
{ok, {SupFlags, Children}}.Elixir/OTP
Elixir uses the same OTP supervision concepts with Elixir child specs and supervisor modules. This is a simplified local supervisor; real applications often add richer child specs, names, and shutdown options.
defmodule ServiceCluster.Supervisor do
use Supervisor
def start_link(init_arg) do
Supervisor.start_link(__MODULE__, init_arg, name: __MODULE__)
end
@impl true
def init(_init_arg) do
children = [
%{
id: DatabasePool,
start: {DatabasePool, :start_link, [[]]},
restart: :permanent
},
%{
id: CacheLayer,
start: {CacheLayer, :start_link, [[]]},
restart: :permanent
},
%{
id: ApiHandler,
start: {ApiHandler, :start_link, [[]]},
restart: :transient
}
]
Supervisor.init(children,
strategy: :one_for_one,
max_restarts: 5,
max_seconds: 60
)
end
endGo
Go has no supervision primitive in the standard language/runtime. The example below is a simplified restart loop — a production implementation would also need graceful shutdown, time-windowed budgets, strategy variants, or a library/framework.
type Supervisor struct {
maxRestarts int
restarts int
mu sync.Mutex
}
func (s *Supervisor) monitor(name string, start func() error) {
for {
err := start()
if err == nil {
return // Clean exit
}
s.mu.Lock()
s.restarts++
if s.restarts > s.maxRestarts {
s.mu.Unlock()
log.Fatalf("supervisor: %s exceeded restart limit", name)
}
s.mu.Unlock()
log.Printf("supervisor: restarting %s after error: %v", name, err)
}
}
func main() {
sup := &Supervisor{maxRestarts: 5}
go sup.monitor("db", startDatabasePool)
go sup.monitor("cache", startCacheLayer)
go sup.monitor("api", startApiHandler)
select {} // Block forever
}What this shows
Developer experience
Hew makes supervision policy visible in source with restart strategies and budgets. Go leaves restart behavior to application code or libraries, so the policy is defined by the monitoring loop and its surrounding state.
Debugging
Hew's supervisor declarations are visible in source code — you can see every child's restart policy at a glance. In Go, supervision logic is scattered through goroutine management code, and it's common for restart limits to be hardcoded constants buried in a different package.
Trade-offs
Hew expresses restart strategy and budgets as language constructs in its native runtime. The Go example uses an application-owned monitoring loop and mutex-protected restart count; a production implementation would add the required time window, backoff, child-ordering strategy, and shutdown behavior.