Articles
Akka Typed Interview Questions: Mailboxes, Supervision, and Delivery Guarantees
Practice Akka Typed interview questions through mailbox overload, supervision traces, message ordering, and safe retries after a lost acknowledgment.
An actor commits a payment, throws an exception before replying, and restarts. The caller sees a timeout and sends the command again. Did supervision prevent a duplicate payment? No answer about mailboxes alone can resolve that question. You need to separate actor execution, behavior recovery, message delivery, and the external side effect.
This guide uses original interview exercises to make those boundaries visible. Official facts are linked to the Akka Typed 2.10.23 documentation, checked on September 30, 2026. Design inference is labeled separately. There are no candidate reports and no claim about a particular employer's interview format. State your application's Akka version and whether it uses Typed or Classic: a default from one API is not automatically a default of the other.
For a practical starting point, use Debug a Concurrent Job Scheduler and explain which state belongs to one actor, which work survives a restart, and which acknowledgments mean completion.
Does one actor mean one dedicated thread?
A mailbox is a place for messages awaiting processing; a dispatcher schedules actor work. The important reasoning boundary is serial processing of an actor's messages, not ownership of one permanent operating-system thread. An actor can use dispatcher threads without allowing two ordinary message handlers to mutate its private state concurrently.
Original exercise: a worker receives StartJob(7), begins an asynchronous API call, and returns. Before the API call completes, it receives CancelJob(7). Can the completion callback safely update a mutable field directly?
Design inference: confinement works only when state transitions stay inside the actor's message-processing boundary. Send the result back as a message, for example through Typed's pipeToSelf, and let the actor decide whether that result is still relevant. An external callback should not silently become a second writer to actor-owned state. See the official interaction patterns for the supported asynchronous patterns.
The actor should retain a job identifier or generation number. If cancellation moves job 7 to a terminal state, a late success for that generation must not restore it to running. If job identifiers can be reused, the generation prevents an old result from being mistaken for a new request.
Blocking work raises another issue. A synchronous database call holds a dispatcher thread even though the actor itself processes one message at a time. Other actors sharing the dispatcher may lose capacity. Isolate unavoidable blocking work according to the application's dispatcher configuration; adding actors does not necessarily add runnable threads. Measure queued work and execution delay separately.
What does a bounded mailbox actually guarantee?
Official fact: Akka Typed's mailbox documentation describes the default unbounded mailbox and configurable bounded choices. The specific choice matters: a nonblocking bounded mailbox discards overflow to dead letters, while other mailbox implementations can have different push-timeout behavior.
Do not answer “bounded mailboxes block senders” as a universal rule. Name the configured mailbox and its overflow behavior. Likewise, do not answer “tell succeeded, so the command was accepted.” A fire-and-forget send is not a business admission acknowledgment.
Original exercise: a single actor handles 100 commands per second. Producers send 160 per second. Its mailbox can hold 300 queued commands. Under a simple steady-rate approximation, the excess is 60 per second and the queue fills in five seconds. Capacity delays overload; it does not remove the rate mismatch.
A useful response distinguishes three policies. The actor might reject new commands through an explicit protocol, producers might wait for credits before sending more, or the system might discard replaceable updates. Choose based on the meaning of the message. Losing a redundant temperature update and losing a funds-transfer command require different recovery designs.
The admission protocol needs its own failure rules. A producer that waits forever for one credit can stop making progress if the actor terminates. Timeouts, actor termination observation, and reconnect behavior should release or recover that producer state. A credit protocol also needs limits on outstanding work, because acknowledgment of admission is not necessarily acknowledgment of completion.
Watch queue age, not only queue depth. A small mailbox can still contain a command stuck behind a slow handler. Record the oldest pending age, accepted and rejected counts, handler duration, and downstream latency. Dead letters are useful diagnostic evidence, but treating them as a durable recovery queue would be a separate unsupported assumption.
What happens when an Akka Typed actor throws?
Official fact: an unexpected exception stops a Typed actor by default unless supervision is configured. This differs from the Classic default described in the same fault-tolerance guide. Validate an ordinary bad command through the protocol rather than using an exception as routine control flow.
Supervision can wrap a behavior with a strategy such as restart for a selected exception. Restart is a recovery decision about behavior. It does not undo a network request, a database commit, or an email already sent. Ordinary in-memory state must be considered separately from durable state.
An original failure trace makes the distinction concrete:
| Step | Actor-local observation | External observation |
|---|---|---|
| Receive command 42 | Handler begins | No effect yet |
| Commit command 42 | May hold a result locally | Database has committed |
| Throw before acknowledgment | Handler fails | Commit remains |
| Restart supervised behavior | Ordinary state is reinitialized | Commit still remains |
| Caller retries command 42 | New message arrives | Duplicate risk unless deduplicated |
The failed message is not automatically a business retry simply because the behavior restarts. A restarted actor can process subsequent mailbox messages, but your protocol must decide what to do with an operation whose outcome is uncertain. Avoid conflating “mailbox remains available” with “the failed command is replayed.”
Where initialization lives matters. Put state that should be recreated within the behavior setup being restarted. Capturing a mutable object outside that boundary can accidentally preserve it. Conversely, reinitializing a counter does not recover an authoritative business balance. In an interview, draw the restart boundary around the actual behavior factory rather than around every object in the process.
A restart policy also needs limits. A permanently invalid configuration can fail every initialization. Bounded retries or backoff protect shared resources, but cannot turn an invalid credential into a valid one. Name which exception is recoverable and which signal should reach an operator or parent actor.
Are actor messages delivered exactly once and in order?
Official fact: ordinary Akka messaging has at-most-once delivery semantics. Ordering is scoped to messages sent directly from a particular sender to a particular receiver, subject to the documented qualifications; it is not a total order across senders. The message-delivery reliability guide is the relevant contract.
Original exercise: sender A sends SetLimit(10) and then SetLimit(20). Sender B sends Charge(15). Even if A's two messages preserve their sender-receiver order, where can B's command appear? The system has not promised one global ordering for all three messages. Your business protocol must make that ambiguity acceptable or establish a stronger ordering mechanism.
Forwarding through intermediate actors changes the reasoning boundary. Do not infer an end-to-end order from one direct hop. Priority mailboxes and asynchronous work can also make processing or completion order differ from simple arrival intuition. State which order matters: send order, mailbox enqueue order, handler order, durable commit order, or reply order.
An ask timeout is another knowledge boundary. It tells the caller that the expected reply did not arrive within its deadline. Inference: that alone does not prove the receiver failed to run the command. The timeout can occur before execution, during execution, after commit, or while the reply is lost or delayed.
If a library supplies reliable delivery, explain the acknowledgment and redelivery mechanism actually being used. Do not silently extend ordinary tell semantics to that protocol. A delivery acknowledgment may confirm reception without confirming an external transaction; stronger business guarantees require a business-level contract.
How can command 42 be made safe to retry?
Use a stable command identifier across retries. Store the command's fingerprint, committed result, and deduplication record with the side effect in a durable atomic boundary where possible. On a matching retry, return the saved result; on an identifier reused for a different command, reject it explicitly.
This is a design recommendation, not a guarantee supplied by Akka's mailbox. If the actor's deduplication map exists only in memory, restart loses it. If the database commit and dedup record are separate operations, a crash between them still leaves a duplicate window. Put the state transitions next to the side effect in your design diagram.
Expiration is part of the contract. Removing dedup records after one minute is unsafe if clients may legitimately retry an hour later. Retention must cover the advertised retry horizon, or the system must reject expired identifiers in a way that avoids treating an old retry as a fresh command.
Concurrent retries need a rule too. Two callers can reach different actor instances or the same durable backend during a failover. A uniqueness constraint or equivalent atomic acquisition can make one operation authoritative. Actor-local serial processing is helpful, but it does not establish a global invariant if routing, deployment, or storage has multiple writers.
What tests expose the real guarantee?
Write a test that commits the side effect and suppresses the reply. Retry the same command and verify one side effect and the same result. Then restart the actor between those steps. This probes durable deduplication rather than merely checking that a handler returned an acknowledgment.
Test two different payloads with the same identifier, simultaneous retries, and a record at the expiration boundary. These cases force you to specify the protocol rather than relying on a happy-path map lookup. For asynchronous jobs, deliver a stale completion after cancellation and verify it cannot reverse the terminal state.
For supervision, inject a selected recoverable failure and observe the recreated local state, the next queued command, and the failed command's outcome. Separately inject an ordinary validation error and verify a protocol response without an unintended restart. Avoid arbitrary sleeps: explicit probes, controlled callbacks, and observable messages make the boundary easier to verify.