> For the complete documentation index, see [llms.txt](https://tochemey.gitbook.io/goakt/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://tochemey.gitbook.io/goakt/features/actor/routers.md).

# 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`](https://github.com/Tochemey/goakt/blob/main/protos/goakt/goakt.proto#L144-L150) message.
2. Send that [`Broadcast`](https://github.com/Tochemey/goakt/blob/main/protos/goakt/goakt.proto#L144-L150) 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`](https://github.com/Tochemey/goakt/blob/main/protos/goakt/goakt.proto#L144-L150) 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`](https://github.com/Tochemey/goakt/blob/main/protos/goakt/goakt.proto#L237-L243) 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`](https://github.com/Tochemey/goakt/blob/main/protos/goakt/goakt.proto#L144-L150) 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`](https://github.com/Tochemey/goakt/blob/main/protos/goakt/goakt.proto#L144-L150) 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`](https://github.com/Tochemey/goakt/blob/main/protos/goakt/goakt.proto#L237-L243) 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`](https://github.com/Tochemey/goakt/blob/main/protos/goakt/goakt.proto#L144-L150) sender; the router never blocks.

#### Configuration

* Use the `SpawnRouter` option `AsScatterGatherFirst` when creating the router.

#### Messaging pattern

* Wrap your payload in [`Broadcast`](https://github.com/Tochemey/goakt/blob/main/protos/goakt/goakt.proto#L144-L150) 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`](https://github.com/Tochemey/goakt/blob/main/protos/goakt/goakt.proto#L144-L150) 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`**](https://github.com/Tochemey/goakt/blob/main/protos/goakt/goakt.proto#L245-L258) 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`](https://github.com/Tochemey/goakt/blob/main/protos/goakt/goakt.proto#L260-L280) message.
* [**`AdjustRouterPoolSize`**](https://github.com/Tochemey/goakt/blob/main/protos/goakt/goakt.proto#L282-L314) 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.
