Routers

Routers let you send the same kind of message to a group of actors, so the work can be processed in parallel. They’re powerful, but they should be used with care: depending on the router type and workload, they can actually hurt performance instead of helping it.

When a router receives a message to broadcast, it:

  1. Checks each routee to see if it is still alive.

  2. Removes any dead routees from its internal set.

  3. Stops itself automatically once the last routee has stopped.

Router Types

GoAkt currently provides pool routers and ships with three router types:

  • Standard

  • TailChopping

  • ScatterGatherFirst

All routers are regular actors and are spawned through ActorSystem.SpawnRouter. Routers and their routees are not relocatable.

Once a router is created, you typically:

  1. Wrap your actual payload in a Broadcast message.

  2. Send that Broadcast to the router.

  3. The router then applies its strategy and forwards the payload to its routees.


Standard Router

The Standard router simply forwards messages to its routees according to a routing strategy. It does not send replies back to the original sender.

Supported strategies

  • Fan-Out: Broadcasts the message to all available routees in parallel.

  • Random: Picks one routee at random from the available set and sends the message to it.

  • Round-Robin: Distributes messages evenly across routees in a round-robin fashion. For n messages and k routees, each routee will receive approximately n/k messages.

Usage notes

  • A router is just like any other actor that can be spawned via ActorSystem.SpawnRouter.

  • Wrap your message in Broadcast and send it via Tell:

    • The router forwards the message according to its strategy.

    • No reply is expected.

Important:Do not use Ask when sending messages to a Standard router. Standard routers are intended for fire-and-forget message patterns.


TailChopping Router

The TailChopping router optimises for predictable latency over full concurrency.

Instead of hitting all routees at once, it:

  1. Picks one routee at a time, in random order.

  2. Waits up to a configured timeout for a reply.

  3. If no reply arrives in time, it moves on to the next routee.

  4. Stops once it receives the first successful reply or the overall deadline is reached.

  5. If no reply is received after the configure time budget, a StatusFailure message is sent as response.

This avoids “thundering herd” effects while still racing to find any healthy worker within a bounded time.

Behavior

  • Requests are sent sequentially (one routee at a time).

  • Replies are forwarded asynchronously back to the Broadcast sender.

  • The router does not block waiting for results; it just forwards replies when they arrive.

Configuration

  • Use the SpawnRouter option AsTailChopping when creating the router.

Messaging pattern

  • Wrap your payload in Broadcast and send it to the router via Ask.

  • The caller gets a future/response with the first successful reply (or a timeout).

Important: ✔ All routees used by a TailChopping router are expected to follow the Ask-pattern when handling messages, so that replies are produced in a timely, predictable way.


ScatterGatherFirst Router

The ScatterGatherFirst router optimizes for low latency and resilience by trading some efficiency for speed:

  1. Sends the same request to all live routees concurrently.

  2. Waits for the first successful reply within a configured time budget (within).

  3. Returns that first result and ignores late replies.

  4. If no reply is received after the configure time budget, a StatusFailure message is sent as response.

Behavior

  • Requests are broadcast to all routees at once.

  • As soon as one routee responds successfully, that reply is forwarded to the sender.

  • Any later replies are discarded once the first result has been returned or the deadline has elapsed.

  • Replies arrive asynchronously to the Broadcast sender; the router never blocks.

Configuration

  • Use the SpawnRouter option AsScatterGatherFirst when creating the router.

Messaging pattern

  • Wrap your payload in Broadcast and send it to the router via Ask to receive the first successful reply.

Important: ✔ All routees used by a ScatterGatherFirst router must also use the Ask-pattern when handling messages, ensuring they can produce replies that the router can race.


Supervision

GoAkt routers are supervised so that both the router itself and its routees can recover from failures in a controlled way.

  • Router actor Routers themselves resume processing Broadcast messages in case of failure. This keeps the router available even if a particular message causes an error.

  • Routees / workers Routees are supervised with a OneForOneStrategy using the Escalate directive by default. Instead of the router deciding what to do, failures are escalated — and the creator of the router can then define, via RouterOption at creation time, what should happen when a given worker/routee is faulty.

To handle faulty routees, you can configure the router with one of the following options:

  • WithResumeRouteeOnFailure Resume the faulty worker/routee, keeping its state and allowing it to continue processing subsequent messages.

  • WithRestartRouteeOnFailure Restart the faulty worker/routee. This typically means stopping it and creating a fresh instance, clearing any bad in-memory state.

  • WithStopRouteeOnFailure (default) Stop the faulty worker/routee. The router removes it from its routee set; depending on your configuration, you might then rely on pool sizing or external logic to replace it.

By choosing the appropriate RouterOption, you can tune each router for your workload:

  • Use resume when failures are transient and stateful workers are expensive to rebuild.

  • Use restart when state is cheap and you want a clean slate after each failure.

  • Use stop when a failing worker should be removed from the pool and possibly replaced by other mechanisms.


Management

Routers can also be managed at runtime by sending special control messages to the router actor. These allow you to inspect and adjust the routee pool without recreating the router.

Supported management messages:

  • GetRoutees Request a snapshot of the router’s current routee set. The router replies with its current pool composition (e.g. the list of routee names) via the Routees message.

  • AdjustRouterPoolSize Request a delta resize of the router’s routee pool at runtime. This message changes the pool size by a given delta:

    • Positive values grow the pool by spawning additional routees.

    • Negative values shrink the pool by stopping some existing routees.

These management messages make it possible to:

  • Inspect which workers are currently in the pool.

  • Scale the pool up under heavy load or down when traffic is low.

  • React to operational signals (metrics, alerts) without restarting the system.


Summary

  • Use Standard routers for fire-and-forget scenarios with flexible routing strategies (Fan-Out, Random, Round-Robin). Use Tell, no Ask.

  • Use TailChopping routers when you want predictable latency and want to avoid overloading all routees at once. Use Ask.

  • Use ScatterGatherFirst routers when you want the fastest possible response from any routee and can afford to send the request to all of them in parallel. Use Ask.

  • Use Supervision options (WithResumeRouteeOnFailure, WithRestartRouteeOnFailure, WithStopRouteeOnFailure) to define how faulty routees are handled.

  • Use Management messages (GetRoutees, AdjustRouterPoolSize) to inspect and dynamically resize router pools at runtime.

Always be mindful of the cost of broadcasting and routing: more concurrency isn’t always better, and routers should be introduced where they clearly improve throughput, latency, resilience, or manageability.

Last updated