Store and forward in LoT connectors
Store and forward means holding messages while the next hop is slow or unavailable, then passing them on when it catches up. In Coreflux, every LoT connector sits behind a shared, bounded, in-memory queue between the broker and the connector’s handlers.ADD ROUTE_CONFIG decides what each connector does when that queue is full: wait (backpressure) or drop the newest message.
The principles, and how LoT handles them
What it does not do
Filling the gaps
- Upstream, use MQTT itself. Publish with QoS 1 or 2 from clients with persistent sessions (
cleanSession=false, or a session expiry in MQTT 5), so the broker keeps their messages while they are offline. The broker’s durability settings decide what survives a broker restart. - Store first, forward later. Write each message to a database connector (or a model with
STORE IN), then forward from that table on a schedule. The table is the durable buffer and can be replayed. - Let the destination buffer. Kafka, and a remote MQTT broker with persistent sessions, keep their own durable queues once a message reaches them.
- Watch for drops. The broker logs when a connector starts dropping. Treat that as an alert.
When to use it
- Leave the default (
balanced) on most connectors. - Use
losslesswhen a path must not lose messages (audit, compliance) and you accept that the publisher may wait. - Use
realtimewhen high-rate telemetry must never stall the client that published it. - Set
OVERLOAD_CAPACITYto cap one noisy connector so it cannot crowd out others.
ADD ROUTE_CONFIG is the only LoT block that works on every connector 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
- Never drop a message
- Never stall the publisher
- Cap a noisy connector
Use
lossless on a forwarding path that must keep every message. The publisher waits until this connector has room — it never drops.Settings
All three settings are optional. An absentADD ROUTE_CONFIG block inherits the broker default entirely.
A message already accepted for a connector is never evicted. Only the arriving message can be dropped. That keeps sequences such as Sparkplug birth then data self-consistent under load.
Defaults
Each setting resolves on its own:- No
ADD ROUTE_CONFIGblock — the connector inherits the broker default for policy, timeout, and capacity. - A block is present but a setting is omitted — that setting does not copy the broker’s timeout or capacity.
OVERLOAD_POLICYinherits the broker default.OVERLOAD_TIMEOUTandOVERLOAD_CAPACITYuse that policy’s own default (50ms forbalanced, unbounded capacity). - An invalid value (unknown policy name, negative timeout, non-positive capacity) does not stop the connector loading. The broker logs an error and uses that field’s default.
ADD ROUTE_CONFIG always overrides these for that connector.
OVERLOAD_CAPACITY on a connector is a private budget for that connector. The shared ceiling is separate: one connector can hit its own cap and start dropping while other connectors still have room.
See Broker Configuration for how these sit alongside other broker limits.
What happens under load
When a connector 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 connector. The broker logs two different saturation conditions (each at most once every five seconds):- The shared connector queue is full — every connector waiting behind it is affected.
- A single connector is over the
OVERLOAD_CAPACITYit declared for itself — the shared queue may still have room.
Troubleshooting
A connector drops messages even though the broker looks idle
A connector drops messages even though the broker looks idle
That connector’s own
OVERLOAD_CAPACITY is exhausted. Look for the broker warning that a connector is over its in-flight budget, not the warning that the shared queue is full.Publishers on a lossless connector stall under load
Publishers on a lossless connector 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 connector with OVERLOAD_CAPACITY so it drops its own overflow instead.OVERLOAD_CAPACITY seems to have no effect
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.A connector with no wildcard still waits longer than its own policy
A connector with no wildcard still waits longer than its own policy
Another connector’s overlapping wildcard pattern resolved a stricter policy for that topic. See the overlapping-pattern warning above.
The connector drops immediately and never waits
The connector drops immediately and never waits
The resolved policy is
realtime. That is expected — realtime drops without waiting.Best practices
- Start with the default
balancedpolicy. Change a connector only when you have a reason (must not lose, must not stall, or must not crowd others). - Use
losslesssparingly. It protects delivery at the cost of delaying the publisher that caused the backlog. - Put
OVERLOAD_CAPACITYon the noisy connector, not on the critical one — the critical connector can stay unbounded while the noisy connector drops its own overflow. - Prefer exact topic mappings when two connectors would otherwise share a broad
+/#pattern with different policies. - Invalid settings never fail the connector silently: read the broker log after deploy if behavior does not match the block you wrote.
Next Steps
Connectors Overview
Define connectors, mappings, and status topics.
Broker Configuration
Set broker-wide queue defaults and other limits.

