Configuring Loss Injection
Loss injection is how you stress the recovery path before production does it for you. The Java media driver lets you bake a loss rate into the driver at JVM startup through system properties — deterministic and reproducible, which is exactly what automated tests and CI need.
This page is about wiring up that static loss injection and reading its effect. For how Aeron actually detects gaps and retransmits, defer to The Aeron Files. For the same io.aeron.driver.ext mechanism alongside CUBIC congestion control, see Loss Injection & CUBIC Congestion Control.
Static configuration via system properties
Section titled “Static configuration via system properties”Set loss parameters at JVM startup. These properties are consumed by DebugChannelEndpointConfiguration.
The rate properties do nothing on their own — the default channel endpoints never consult a loss generator. You must first swap in the debug endpoints, which is a separate property per side:
-Daeron.ReceiveChannelEndpoint.supplier=io.aeron.driver.ext.DebugReceiveChannelEndpointSupplier-Daeron.SendChannelEndpoint.supplier=io.aeron.driver.ext.DebugSendChannelEndpointSupplier| Property | Description |
|---|---|
aeron.debug.receive.data.loss.rate | Receive data loss rate (0.0 - 1.0) |
aeron.debug.receive.data.loss.seed | Seed for receive data loss RNG |
aeron.debug.receive.control.loss.rate | Receive control loss rate |
aeron.debug.receive.control.loss.seed | Seed for receive control loss RNG |
aeron.debug.send.data.loss.rate | Send data loss rate |
aeron.debug.send.data.loss.seed | Seed for send data loss RNG |
aeron.debug.send.control.loss.rate | Send control loss rate |
aeron.debug.send.control.loss.seed | Seed for send control loss RNG |
You get four independent loss knobs: send/receive crossed with data/control. The most interesting tests come from asymmetric configurations.
Four design points that make loss injection realistic
Section titled “Four design points that make loss injection realistic”The framework rests on four deliberate design decisions.
1. Data and control loss are independent
Section titled “1. Data and control loss are independent”Data packets and control packets — status messages, NAKs, RTT — have separate loss generators. This lets you test “data gets through but NAKs are lost.” That scenario probes whether the system recovers when its recovery mechanism itself is impaired.
2. Two loss models — random rate vs. scripted gaps
Section titled “2. Two loss models — random rate vs. scripted gaps”The *.loss.rate properties above wire to RandomLossGenerator, whose per-frame decision is literally random.nextDouble() <= lossRate. It is not retransmission-aware: at a given rate it will drop retransmissions at that same rate — realistic for a lossy link, but it means a high rate can stack retransmits.
The package also ships FixedLossGenerator and MultiGapLossGenerator, which are retransmission-aware: they track which frames have already been dropped per stream and session and let the retransmission through. Without that, a “drop frame #42” rule would also drop the retransmission of #42 — an unrecoverable gap and an infinite retransmission loop. But the system-property mechanism never instantiates these two — they’re for programmatic setups (e.g. JavaTestMediaDriver). Use the properties for uniform random loss; reach for the programmatic path when you need a scripted, retransmission-aware gap pattern.
3. Seeds make runs deterministic
Section titled “3. Seeds make runs deterministic”Every random loss generator accepts a seed. A fixed seed means the same loss pattern means the same test outcome. This is what makes flaky-looking tests reproducible — critical for CI/CD where you need consistent pass/fail behavior.
4. NAK coalescing can be disabled
Section titled “4. NAK coalescing can be disabled”Call dontCoalesceNaksOnReceiverByDefault() to set NAK delay to zero. NAKs are sent immediately instead of waiting for the coalescing window. This speeds up retransmission in tests without changing the loss behavior.
Counters to watch when loss is injected
Section titled “Counters to watch when loss is injected”These four counters tell you how the system detects, recovers, and pays for loss.
| Counter | What it tells you |
|---|---|
| NAK messages sent/received | Spikes when loss is active — shows detection speed |
| Retransmits sent | Shows recovery activity — how much bandwidth is spent on recovery |
| Publisher pos vs Subscriber pos | Gap grows under loss — shows how far behind the subscriber falls |
| Bytes sent/received | Throughput impact — quantifies the cost of loss |
How loss maps to p50, p99, and throughput
Section titled “How loss maps to p50, p99, and throughput”Loss injection moves all three numbers, but not evenly.
- Throughput takes the most direct hit. Retransmits sent compete for the same bandwidth as fresh data, and the bytes sent/received counters quantify that cost in real time.
- p99 and the tail stretch first. A dropped frame stalls the subscriber until detection and retransmission complete. NAK detection speed and the coalescing window set how long that stall lasts — which is exactly why
dontCoalesceNaksOnReceiverByDefault()shortens it. - p50 stays comparatively stable at low loss rates, because most messages arrive on the first try. It degrades only as loss climbs high enough to affect the median delivery.
This site is not affiliated with, endorsed by, or sponsored by Adaptive Financial Consulting Limited or the Aeron project. Aeron is a registered trademark of Adaptive Financial Consulting Limited.
Aeron is a trademark of Adaptive Financial Consulting Limited in the United Kingdom and other countries.