feat(tcp): guard against send buffer overflow in TCP connections#2472
Open
dirkwa wants to merge 4 commits intoSignalK:masterfrom
Open
feat(tcp): guard against send buffer overflow in TCP connections#2472dirkwa wants to merge 4 commits intoSignalK:masterfrom
dirkwa wants to merge 4 commits intoSignalK:masterfrom
Conversation
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 usingLatestValuesAccumulator(keeping only the latest value per path/source). When the socket drains, accumulated values are flushed with a$backpressureindicator so clients know data was consolidated. A hard-kill viasocket.destroy()fires when the buffer exceedsMAXSENDBUFFERSIZEfor longer thanMAXSENDBUFFERCHECKTIME.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 deprecatedsocket.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 —
writableLengthexceeded the threshold after ~2.6 MB of buffered data andsocket.write()correctly returnedfalse, 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.