Implementing Backpressure Handling
1. Understanding Backpressure
Backpressure occurs when a producer outpaces a consumer's drain rate, causing buffers to grow and memory pressure to rise. In WebSockets the symptom is bufferedAmount climbing toward unbounded.
| Layer | Buffer |
|---|---|
| App outbox | Your queue |
| Browser send | ws.bufferedAmount |
| TCP send | Kernel socket buffer |
| Server receive | ws library buffer |
2. Monitoring bufferedAmount Property
Example: Drain detection
function whenDrained(ws) {
return new Promise(res => {
const tick = () => ws.bufferedAmount === 0 ? res() : setTimeout(tick, 50);
tick();
});
}
| Property | Semantics |
|---|---|
bufferedAmount | Bytes queued in UA, not yet on wire |
| Polling | No native drain event — poll or use timer |
3. Implementing Flow Control
| Mechanism | Detail |
|---|---|
| Credit-based | Server grants N tokens; client decrements |
| Window ack | Sender pauses until ack of last N |
| Pause/resume msg | Receiver tells sender to stop |
4. Using High Water Mark
Example: HWM gating
const HWM = 256 * 1024; // 256 KB
function canSend() { return ws.bufferedAmount < HWM; }
| Threshold | Action |
|---|---|
| Low water | Resume producer |
| High water | Pause producer |
| Critical | Drop / close |
5. Dropping Messages
| Strategy | Best For |
|---|---|
| Drop newest | Order matters more than freshness |
| Drop oldest | Latest data (cursor, prices) |
| Sample (1/N) | Telemetry |
| Coalesce | Idempotent updates (state replace) |
6. Implementing Message Prioritization
Example: Priority lanes
const lanes = { hi: [], mid: [], lo: [] };
function flush() {
for (const lane of ["hi","mid","lo"]) {
while (lanes[lane].length && canSend()) ws.send(lanes[lane].shift());
}
}
| Priority | Example |
|---|---|
| Hi | Auth, control, errors |
| Mid | User actions |
| Lo | Telemetry, analytics |
7. Handling Slow Clients
| Server Action | Detail |
|---|---|
| Per-client buffer cap | Drop / close when exceeded |
| Timeout slow drains | Close 1013 |
| Demote subscription | Lower update rate |
| Snapshot + delta | Skip back-to-snapshot |
8. Implementing Client-Side Buffering
| Bound | Reason |
|---|---|
| Size limit | Cap memory |
| Age limit | Avoid stale |
| Count limit | Predictable drain time |
9. Detecting Backpressure Conditions
| Signal | Threshold |
|---|---|
bufferedAmount rising | Δ > 0 over N seconds |
| Pong RTT spike | 2× baseline |
| Queue depth | App outbox > N |
10. Testing Backpressure Scenarios
| Test | How |
|---|---|
| Throttle network | DevTools Slow 3G |
| Pause consumer | Suspend message handler |
| Burst producer | Send 10k msgs / sec |
| Memory probe | performance.memory |