ZeroTrace AirLeak
Throttle & Performance
How the event throttle works, and how to tune it
The event throttle is the most important control for managing capture-stream pressure. This page explains what the throttle does, what it doesn't do, and how to tune it.
What the throttle is
The throttle is a per-(MAC, event-type) cooldown on the USB-CDC event stream. After an event for a given device + event type is streamed, any further event of the same type from the same MAC is suppressed from the USB stream until the cooldown expires.
Default cooldown: 1000 ms (1 second).
So: if Sarah's iPhone sends an Apple Continuity Nearby Info advertisement, the desktop sees it as one EV: line. The next 5 advertisements over the next second don't get streamed individually — they're rolled up into the device aggregator silently. After 1 s, the next one streams.
What the throttle is NOT
The throttle does not affect:
- The device aggregator — every captured event updates
last_seen, RSSI EMA, observation count, classification, and any field the parser surfaced. The aggregator is fully aware of every event. - The alert engine — every event runs through every armed alert rule. An AirDrop event during throttle cooldown still fires
airdrop_discoverable. - Heartbeat counters —
events,wifi_events,ble_eventsall count throttled events.events_throttledcounts the suppressions separately. - The fingerprint merger — fingerprints update on every event, throttled or not.
In short: the throttle controls what the desktop sees, not what the unit knows. Every byte the radio captured is still processed and reflected in the aggregator state.
Why the throttle exists
USB-CDC at 921600 baud has a hard ceiling around 100 KB/sec sustained. A typical decoded event encodes to 200–400 bytes. That's ~250 events/sec maximum sustained, before the desktop's read-side buffer fills and we drop bytes.
Some real-world densities:
| Environment | BLE peers | Events/sec without throttle | Events/sec with 1 s throttle |
|---|---|---|---|
| Empty room | 5 | ~25 | ~10 |
| Home | 15 | ~80 | ~25 |
| Office | 40 | ~250 | ~50 |
| Café / coworking | 60 | ~400 | ~70 |
| Conference / airport | 100+ | ~600+ | ~100 |
Without throttle, dense environments saturate the USB pipe and the desktop drops events. With throttle, the unit and desktop stay in sync — every device gets aggregator updates, the desktop sees one event per device per second.
When to change the throttle
Lower it (or set to 0) when:
- You're debugging a specific behavior and need to see every advertisement
- You're capturing a single nearby device and want fine-grained timing
- You're in a sparse environment (empty room, few devices)
Raise it when:
Drop %in the status bar is consistently > 1 %- You're in a very dense environment (50+ devices)
- You're in a long capture and don't need second-level updates
- You're streaming over an unreliable USB connection (cable or hub issues)
Throttle settings vs experience
| Throttle | Visible behavior |
|---|---|
0 (firehose) | Every event streams. RSSI charts update super-smoothly. ~500 events/s peak. Don't run this for >5 minutes in dense environments — you'll fill log buffers and may overrun the desktop. |
250 ms | Up to 4 updates per device per second. Smooth charts, manageable rate. |
500 ms | Up to 2 updates per device per second. Charts feel responsive, ~half the events streamed compared to 250 ms. |
1000 ms (default) | One update per device per second. Charts update smoothly. Heart of normal use. |
2000 ms | One update every 2 s per device. RSSI charts stutter slightly. Useful in saturated environments. |
5000 ms | Per-device updates only every 5 s. Mostly stable readings; loses fast transients. |
Reading throttle stats
In the heartbeat:
events=4421 events_throttled=8902 drop_pct=0.0%
This says:
- 4,421 events were streamed to USB
- 8,902 events were suppressed by the throttle
- 0 events were lost on USB transmit (drop_pct = 0)
Total events captured: 4,421 + 8,902 = 13,323. The unit processed every one, the desktop saw 4,421 of them, and none were lost.
The throttle is healthiest when drop_pct stays at 0 % even as events_throttled grows. That's the throttle absorbing pressure correctly.
Tuning by symptom
Symptom: Drop % climbing toward 5 %
events=120000 drop_pct=4.3%
The desktop is falling behind on USB read. Raise the throttle to reduce stream pressure.
airleak-throttle 2000
Watch drop_pct over the next minute. If it falls to 0 %, the throttle is now matched to the environment.
Symptom: heap free dropping under 12 %
heap_free_pct=8
The aggregator is full and queues are deep. Raise the throttle — fewer events on the wire means less pressure on transmit queues, more free heap.
If raising the throttle doesn't help, the aggregator itself is full. Issue:
airleak-clear-aggregator
Or wait — LRU eviction handles it automatically.
Symptom: device updates feel sluggish
If RSSI charts stutter or the device-detail page lags, lower the throttle:
airleak-throttle 500
This doubles the per-device update rate. Don't go below 250 ms unless you're in a sparse environment.
Symptom: parser CPU saturation
In airleak-tasks:
parser 92%
The parser is at saturation. Raise the throttle so fewer events queue for emission. Even though parser work is upstream of the throttle, throttled events skip the JSON-encode step (the most expensive parser-task work).
Capacity numbers
Quick reference for what the firmware can sustain:
| Throughput level | Sustained events/sec | Heap pressure | Notes |
|---|---|---|---|
| Light | < 50 | green | Healthy at any throttle |
| Medium | 50–150 | green to yellow | Normal Monitor mode |
| Heavy | 150–250 | yellow | Recommend throttle ≥ 1000 ms |
| Saturated | 250+ | red | Increase throttle or reduce channels |
The unit's hardware ceiling is around 300–350 sustained events/sec on the USB stream before drops begin. Above that, the throttle must take up the slack.
Per-event-type throttle
The throttle bucket is keyed by (MAC, event_type). So:
Sarah's iPhoneApple Continuity nearby_info → throttled at 1 / 1 sSarah's iPhoneApple Continuity AirDrop → separate bucket, also throttled at 1 / 1 s
A single device can stream multiple event types per second without contending. This means a phone that emits both nearby_info and AirDrop and probe-requests can produce 3 events per second, each in its own throttle bucket.
In practice, the throttle is rarely the bottleneck for low-volume types like AirDrop. It's the high-volume types (nearby_info, beacon) that benefit most from throttling.
Alerts ride alongside events on the USB stream as AL: lines, but they have their own per-rule rate limit (default 30 s per device-rule pair). Throttling events does not affect alert delivery — every armed alert that fires reaches the desktop.