Skip to content

feat(tcp): guard against send buffer overflow in TCP connections#2472

Open
dirkwa wants to merge 4 commits intoSignalK:masterfrom
dirkwa:tcp-backpressure
Open

feat(tcp): guard against send buffer overflow in TCP connections#2472
dirkwa wants to merge 4 commits intoSignalK:masterfrom
dirkwa:tcp-backpressure

Conversation

@dirkwa
Copy link
Contributor

@dirkwa dirkwa commented Mar 17, 2026

Fixes #1055

Summary

Signal K TCP server (src/interfaces/tcp.ts): Mirrors the existing WS accumulate-and-flush pattern. During backpressure, incoming deltas are accumulated using LatestValuesAccumulator (keeping only the latest value per path/source). When the socket drains, accumulated values are flushed with a $backpressure indicator so clients know data was consolidated. A hard-kill via socket.destroy() fires when the buffer exceeds MAXSENDBUFFERSIZE for longer than MAXSENDBUFFERCHECKTIME.

NMEA TCP server (src/interfaces/nmea-tcp.js): Uses drop-and-disconnect — NMEA sentences are stateless and cannot be meaningfully accumulated. Writes are skipped when the buffer exceeds the threshold, and the socket is destroyed if the oversized condition persists beyond the timeout.

TCP client (packages/streams/src/tcp.ts): Outbound writes (outEvent, toStdout) are skipped with a debug log when the send buffer is full. This matches the fire-and-forget nature of outbound client writes.

All three paths reuse the same environment variables already used by WS connections: BACKPRESSURE_ENTER, BACKPRESSURE_EXIT, MAXSENDBUFFERSIZE, MAXSENDBUFFERCHECKTIME.

Uses socket.writableLength (the current API) rather than the deprecated socket.bufferSize.

Manual testing:

Tested against a live server receiving real NMEA 2000 data from a YDEN02 gateway (~91 KB/s, 572 delta chunks over 7 seconds). Subscribe/receive/disconnect all work correctly. Backpressure logic was verified in isolation by blasting 64 KB chunks to an unresponsive socket — writableLength exceeded the threshold after ~2.6 MB of buffered data and socket.write() correctly returned false, confirming the detection path works. On localhost the OS TCP receive window (~4 MB) is larger than the default 512 KB threshold, so the threshold is reached after several MB of backed-up data; on real slow clients (WiFi/cellular) it fires much sooner.

dirkwa added 4 commits March 18, 2026 07:00
Guard against send buffer overflow in the Signal K TCP server (port
8375). When a client's send buffer exceeds BACKPRESSURE_ENTER, the
server accumulates only the latest value per path using the existing
LatestValuesAccumulator. When the buffer drains below BACKPRESSURE_EXIT,
accumulated values are flushed with a $backpressure indicator.

A hard-kill safety net destroys the socket if the buffer exceeds
MAXSENDBUFFERSIZE for longer than MAXSENDBUFFERCHECKTIME.

Uses socket.writableLength (modern API) instead of deprecated bufferSize.

Closes SignalK#1055
Guard against send buffer overflow in the NMEA TCP server (port 10110).
NMEA0183 sentences are stateless, so accumulation is not meaningful.
Instead, sentences are dropped for slow clients when the buffer exceeds
BACKPRESSURE_ENTER, and the connection is terminated if it stays above
MAXSENDBUFFERSIZE for longer than MAXSENDBUFFERCHECKTIME.
Guard against send buffer overflow in the TCP client stream. Outbound
writes via outEvent and toStdout are skipped when the socket's
writableLength exceeds BACKPRESSURE_ENTER. The next event will carry
fresh data, so no accumulation is needed.

Includes tests that verify writes are skipped when the buffer is full
by overriding writableLength on the socket.
Update environment variable descriptions and backpressure documentation
to reflect that BACKPRESSURE_ENTER, MAXSENDBUFFERSIZE, and
MAXSENDBUFFERCHECKTIME now apply to TCP connections in addition to
WebSocket. Add connection types table documenting the strategy used
for each interface.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Guard against send buffer overflow in tcp out connections

1 participant