> ## Documentation Index
> Fetch the complete documentation index at: https://docs.coreflux.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Route Overload Configuration

> Choose wait or drop with ADD ROUTE_CONFIG when a LoT route cannot keep up with incoming MQTT

## When MQTT outruns the destination

Incoming MQTT can outpace a database insert, a cloud bridge, or a PLC write. `ADD ROUTE_CONFIG` lets **each route** choose whether to wait or to drop the newest message — without crowding out every other route.

<Tip>
  **Like a ticket window with a limited line.** People already being served always finish. When the line is full you can wait for a spot, wait a short time then turn the newest arrival away, or turn them away immediately.
</Tip>

### When to use it

* Leave the default (`balanced`) on most routes.
* Use `lossless` when a path must not lose messages (audit, compliance) and you accept that the publisher may wait.
* Use `realtime` when high-rate telemetry must never stall the client that published it.
* Set `OVERLOAD_CAPACITY` to cap one noisy route so it cannot crowd out others.

`ADD ROUTE_CONFIG` is the only LoT block that works on **every** route type. Place it next to that type's own config (`SQL_CONFIG`, `SOURCE_CONFIG`, `MODBUS_CONFIG`, and so on). Order among `ADD` blocks does not matter.

***

Let's look at how to set it, starting with the three common contracts.

## Quick Start

<Tabs>
  <Tab title="Never drop a message">
    Use `lossless` on a forwarding path that must keep every message. The publisher waits until this route has room — it never drops.

    ```lot wrap focus={3-4} theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
    DEFINE ROUTE CloudBridge WITH TYPE MQTT_BRIDGE
        ADD ROUTE_CONFIG
            WITH OVERLOAD_POLICY "lossless"
        ADD SOURCE_CONFIG
            WITH BROKER SELF
        ADD DESTINATION_CONFIG
            WITH BROKER_ADDRESS "broker.example.com"
            WITH BROKER_PORT '8883'
            WITH USE_TLS "true"
        ADD MAPPING auditForward
            WITH SOURCE_TOPIC "audit/#"
            WITH DESTINATION_TOPIC "cloud/audit/#"
            WITH DIRECTION "out"
    ```
  </Tab>

  <Tab title="Never stall the publisher">
    Use `realtime` on high-rate telemetry. If the route is full, the arriving message is dropped immediately and the publishing client is not delayed.

    ```lot wrap focus={3-4} theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
    DEFINE ROUTE SensorDB WITH TYPE POSTGRESQL
        ADD ROUTE_CONFIG
            WITH OVERLOAD_POLICY "realtime"
        ADD SQL_CONFIG
            WITH SERVER GET ENV "PG_HOST"
            WITH DATABASE "factory"
            WITH USERNAME GET ENV "PG_USER"
            WITH PASSWORD GET SECRET "PG_PASSWORD"
        ADD EVENT StoreReading
            WITH SOURCE_TOPIC "sensors/+/reading"
            WITH QUERY "INSERT INTO readings (ts, value) VALUES (NOW(), '{value.json}')"
    ```
  </Tab>

  <Tab title="Cap a noisy route">
    Cap how many of **this route's** messages may be in flight. Extra messages from this route drop even while other routes still have room.

    ```lot wrap focus={3-4} theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
    DEFINE ROUTE NoisySensors WITH TYPE MQTT_BRIDGE
        ADD ROUTE_CONFIG
            WITH OVERLOAD_CAPACITY 5000
        ADD SOURCE_CONFIG
            WITH BROKER SELF
        ADD DESTINATION_CONFIG
            WITH BROKER_ADDRESS "telemetry.example.com"
            WITH BROKER_PORT '1883'
        ADD MAPPING sensorsOut
            WITH SOURCE_TOPIC "sensors/#"
            WITH DESTINATION_TOPIC "remote/sensors/#"
            WITH DIRECTION "out"
    ```
  </Tab>
</Tabs>

***

## Settings

All three settings are optional. An absent `ADD ROUTE_CONFIG` block inherits the broker default entirely.

```lot wrap focus={3-6} theme={"theme":"css-variables","languages":{"custom":["/languages/lot.json"]}}
DEFINE ROUTE CloudBridge WITH TYPE MQTT_BRIDGE
    ADD ROUTE_CONFIG
        WITH OVERLOAD_POLICY "balanced"
        WITH OVERLOAD_TIMEOUT 200
        WITH OVERLOAD_CAPACITY 20000
    ADD SOURCE_CONFIG
        WITH BROKER SELF
    ADD DESTINATION_CONFIG
        WITH BROKER_ADDRESS "broker.example.com"
        WITH BROKER_PORT '8883'
        WITH USE_TLS "true"
    ADD MAPPING sensorSync
        WITH SOURCE_TOPIC "sensors/#"
        WITH DESTINATION_TOPIC "cloud/sensors/#"
        WITH DIRECTION "out"
```

| Setting             | Required | Default    | Description                                                                                                                                                                                                 |
| ------------------- | -------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `OVERLOAD_POLICY`   | No       | `balanced` | `lossless` waits until there is room and never drops. `balanced` waits up to `OVERLOAD_TIMEOUT`, then drops the **arriving** message. `realtime` never waits — it drops immediately when the route is full. |
| `OVERLOAD_TIMEOUT`  | No       | `50` (ms)  | Wait budget for `balanced` only. Ignored for `lossless` (waits indefinitely) and `realtime` (never waits). Must be zero or greater.                                                                         |
| `OVERLOAD_CAPACITY` | No       | unbounded  | Maximum number of **this route's** messages allowed in flight at once. Once reached, further messages from this route drop even if other routes still have room. Must be greater than zero.                 |

A message already accepted for a route is never evicted. Only the arriving message can be dropped. That keeps sequences such as Sparkplug birth then data self-consistent under load.

<Warning>
  Do not set `OVERLOAD_CAPACITY` together with `OVERLOAD_POLICY "lossless"`. A capacity cap drops overflow; `lossless` never drops. The broker logs an error naming the route, **ignores the capacity**, and runs the route as lossless. The same conflict applies if the route inherits `lossless` from the broker default and then declares a capacity.
</Warning>

***

## Defaults

Each setting resolves on its own:

1. **No `ADD ROUTE_CONFIG` block** — the route inherits the broker default for policy, timeout, and capacity.
2. **A block is present but a setting is omitted** — that setting does not copy the broker's timeout or capacity. `OVERLOAD_POLICY` inherits the broker default. `OVERLOAD_TIMEOUT` and `OVERLOAD_CAPACITY` use that policy's own default (`50` ms for `balanced`, unbounded capacity).
3. **An invalid value** (unknown policy name, negative timeout, non-positive capacity) does not stop the route loading. The broker logs an error and uses that field's default.

Broker-wide defaults are environment variables, read once at process start. Change them and **restart** the broker. A route's own `ADD ROUTE_CONFIG` always overrides these for that route.

| Variable                                 | Default    | Effect                                                                                                                                                                                                                                                                                                      |
| ---------------------------------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `COREFLUX_ROUTE_OVERLOAD_POLICY`         | `balanced` | Default for `OVERLOAD_POLICY` (`lossless`, `balanced`, or `realtime`). An unrecognized value logs an error and falls back to `balanced`.                                                                                                                                                                    |
| `COREFLUX_ROUTE_CHANNEL_FULL_TIMEOUT_MS` | `50`       | Default wait budget in milliseconds for `balanced`. Ignored when the resolved policy is `lossless` or `realtime`. A negative value logs an error and falls back to `50`.                                                                                                                                    |
| `COREFLUX_ROUTE_CHANNEL_CAPACITY`        | `100000`   | Shared ceiling on how many route updates the broker will hold at once. Below the ceiling, matching messages are accepted. At the ceiling, the arriving message's wait-or-drop policy applies. Values below `1024` are raised to `1024`. Unset, non-numeric, zero, or negative values fall back to `100000`. |

`OVERLOAD_CAPACITY` on a route is a **private** budget for that route. The shared ceiling is separate: one route can hit its own cap and start dropping while other routes still have room.

See [Broker Configuration](/latest/mqtt-broker/configuration#performance-and-back-pressure) for how these sit alongside other broker limits.

***

## What happens under load

When a route is waiting, that wait can delay **the publishing client's own publish** — only that client, and only up to the wait budget. Other connected clients are not stalled by this route.

The broker logs two different saturation conditions (each at most once every five seconds):

* The shared route queue is full — every route waiting behind it is affected.
* A single route is over the `OVERLOAD_CAPACITY` it declared for itself — the shared queue may still have room.

<Warning>
  If two routes could match the same topic (for example one exact mapping and another `sensors/#`), the broker applies the **stricter** wait-or-drop choice among those candidates. A `lossless` route on an overlapping pattern can make a `realtime` route wait, even if only the `realtime` route would have handled the message. The broker never drops a message because a more lenient route asked to drop it. If mixed policies on overlapping wildcards wait or drop more than you expect, check the patterns first — that behavior is by design.
</Warning>

***

## Troubleshooting

<AccordionGroup>
  <Accordion title="A route drops messages even though the broker looks idle">
    That route's own `OVERLOAD_CAPACITY` is exhausted. Look for the broker warning that a route is over its in-flight budget, not the warning that the shared queue is full.
  </Accordion>

  <Accordion title="Publishers on a lossless route stall under load">
    Working as designed — `lossless` waits until there is room. Switch to `balanced` with a timeout if you would rather drop than stall, or cap a noisy route with `OVERLOAD_CAPACITY` so it drops its own overflow instead.
  </Accordion>

  <Accordion title="OVERLOAD_CAPACITY seems to have no effect">
    It is declared alongside `OVERLOAD_POLICY "lossless"` (or the broker default is `lossless`). Check the broker log for the lossless/capacity conflict — capacity is ignored, not applied.
  </Accordion>

  <Accordion title="A route with no wildcard still waits longer than its own policy">
    Another route's overlapping wildcard pattern resolved a stricter policy for that topic. See the overlapping-pattern warning above.
  </Accordion>

  <Accordion title="The route drops immediately and never waits">
    The resolved policy is `realtime`. That is expected — `realtime` drops without waiting.
  </Accordion>
</AccordionGroup>

***

## Best practices

* Start with the default `balanced` policy. Change a route only when you have a reason (must not lose, must not stall, or must not crowd others).
* Use `lossless` sparingly. It protects delivery at the cost of delaying the publisher that caused the backlog.
* Put `OVERLOAD_CAPACITY` on the noisy route, not on the critical one — the critical route can stay unbounded while the noisy route drops its own overflow.
* Prefer exact topic mappings when two routes would otherwise share a broad `+` / `#` pattern with different policies.
* Invalid settings never fail the route silently: read the broker log after deploy if behavior does not match the block you wrote.

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Routes Overview" icon="route" href="./overview">
    Define routes, mappings, and status topics.
  </Card>

  <Card title="Broker Configuration" icon="sliders" href="/latest/mqtt-broker/configuration#performance-and-back-pressure">
    Set broker-wide route overload defaults and other limits.
  </Card>
</CardGroup>
