[grid] Apply TCP backpressure across the WebSocket tunnel handler#17543
[grid] Apply TCP backpressure across the WebSocket tunnel handler#17543shs96c wants to merge 1 commit into
Conversation
Review Summary by QodoApply TCP backpressure across WebSocket tunnel handler
WalkthroughsDescription• Implement TCP backpressure propagation across WebSocket tunnel handler • Mirror channel writability state to peer's read side via setAutoRead • Prevent unbounded outbound buffer growth during bidirectional traffic • Add unit test validating writability transitions and autoRead synchronization Diagramflowchart LR
A["Source Channel<br/>Outbound Buffer"] -->|"exceeds high-water mark"| B["channelWritabilityChanged<br/>Event Fired"]
B -->|"setAutoRead false"| C["Target Channel<br/>Read Paused"]
C -->|"peer stops shipping bytes"| D["Backpressure Applied"]
D -->|"buffer drains below<br/>low-water mark"| E["setAutoRead true"]
E -->|"peer resumes reading"| F["Flow Resumed"]
File Changes1. java/src/org/openqa/selenium/netty/server/TcpTunnelHandler.java
|
Code Review by Qodo
1. Backpressure trips idle close
|
The optional transparent TCP tunnel introduced in 9a2df3a (2026-02-27, "[grid] Router bypass WebSocket data path via transparent TCP tunnel", SeleniumHQ#17146) forwarded inbound bytes from one channel to the other with no flow control. A slow drain on one leg let the opposite leg keep shipping bytes, which Netty queued in the outbound buffer indefinitely. A session with sustained bidirectional traffic and a slow client could grow the Router's heap until the per-channel outbound queue was many megabytes. Hook channelWritabilityChanged on each tunnel handler and mirror the state onto the peer's read side via setAutoRead. When this channel's outbound buffer crosses the high-water mark Netty fires the event and the peer stops reading; when the buffer drains below the low-water mark the peer resumes. The two legs of the tunnel each watch their own writability and toggle the other's read side, so backpressure propagates in both directions. The thresholds come from the channel's existing WriteBufferWaterMark (Netty's default of 32 KiB low / 64 KiB high, or whatever an operator configures). No new numeric cap is introduced. A small EmbeddedChannel test drives the writability transitions both ways and asserts the peer's autoRead flag tracks them. The existing end-to-end TunnelWebsocketTest continues to cover the happy path.
1d537cf to
88b59df
Compare
|
Persistent review updated to latest commit 88b59df |
|
Addressed both qodo findings in 88b59df:
|
Summary
The optional transparent TCP tunnel introduced in #17146 forwarded inbound bytes from one channel to the other with no flow control. A slow drain on one leg let the opposite leg keep shipping bytes, which Netty queued in the outbound buffer indefinitely. A session with sustained bidirectional traffic and a slow client could grow the Router's heap until the per-channel outbound queue was many megabytes.
Hook
channelWritabilityChangedon each tunnel handler and mirror the state onto the peer's read side viasetAutoRead. When this channel's outbound buffer crosses the high-water mark Netty fires the event and the peer stops reading; when the buffer drains below the low-water mark the peer resumes. The two legs of the tunnel each watch their own writability and toggle the other's read side, so backpressure propagates in both directions.The thresholds come from the channel's existing
WriteBufferWaterMark(Netty's default of 32 KiB low / 64 KiB high, or whatever an operator configures). No new numeric cap is introduced.🤖 Generated with Claude Code