Skip to main content
Back to articles
2026 / 06
| 4 min read

What a supervisor restarts in Hew

Following a counter from ordinary messages to a crash and replacement, using the June 2026 Hew release.

hew actors concurrency
On this page

A counter is small enough that we can account for all of its state. Start at zero, add ten, add thirty-two, and ask for the total. Once those operations are messages to an actor, the example also gives us somewhere concrete to ask what happens when that actor stops: which value comes back after a restart, and what happened to the work waiting for it?

The examples here use Hew 0.5.6, released in June. The language has continued to change, so keeping the syntax and runtime together matters when looking back at this version.

actor Counter {
    var count: i64;
    receive fn increment(n: i64) { count = count + n; }
    receive fn total() -> i64 { count }
}

fn main() {
    let counter = spawn Counter(count: 0);
    counter.increment(10);
    counter.increment(32);
    match await counter.total() {
        Ok(value) => println(value),
        Err(_) => println("The counter did not reply"),
    }
}

This prints 42. The counter owns count, and the receive functions describe the messages it handles. In this release, the calls to increment send messages without waiting for a returned value. await counter.total() asks for a reply, and its result has both a success and an error path.

The distinction becomes useful as soon as there is more than one actor. Each can have its own state and mailbox, with messages describing the work to do. This little example has one caller sending to one counter; it doesn’t establish an ordering for messages arriving from several unrelated callers, or anything about machines on a network.

Giving the counters a supervisor

A supervisor adds a recipe for creating children and a policy for replacing them. With the counter above, a declaration can look like this:

supervisor Counters {
    strategy: one_for_one;
    intensity: 3 within 60s;
    child first: Counter(count: 0);
    child second: Counter(count: 0);
}

The two children both begin at zero. one_for_one says to replace the failed child while leaving its sibling running, and 3 within 60s gives the supervisor a rolling restart budget. Three restart attempts are allowed in that window. A further failure while those attempts are still in the window exhausts the budget, rather than starting an unlimited restart loop.

It is worth following the value through a crash. In a small reproduction against this release, the first counter was advanced to 42 and the second to 7. After deliberately crashing the first and obtaining its replacement from the supervisor, the values were:

CounterBefore the crashAfter replacement
First420
Second77

This reproduction was run on 2 October 2026 using the June toolchain. The complete example includes the deliberate crash and a short wait for this demonstration; that wait is not an application-level readiness protocol.

The zero is the important value to account for. The restart implementation constructs a replacement from the child’s initial-state template. It does not recover a snapshot of the state immediately before the crash. The unaffected counter keeps its seven because it wasn’t replaced.

There are other replacement policies:

StrategyChildren replaced after a failure
one_for_oneThe failed child
one_for_allAll children
rest_for_oneThe failed child and those declared after it

The last policy uses declaration order. It can be useful when later children depend on earlier ones, but the runtime isn’t discovering those dependencies for us. The order is part of what the program declares.

The mailbox has a lifetime too

Replacing the actor leaves another question: what about messages that were waiting in its old mailbox? In this version, mailbox teardown drains and frees them. They aren’t automatically replayed into the new child. Pending requests have error handling in the runtime, including an orphan outcome when an unanswered request is discarded, so callers still need to handle an unsuccessful reply.

That means supervision and recovery of application work need to be considered together. For the counter, starting at zero is easy to see. For a worker handling something we intend to keep, we would have to decide where the durable state lives, how unfinished work is found, and whether retrying it could repeat an effect that already happened. Those decisions belong with the operation being performed.