[grid] Add BinaryMessage.wrap for transferring an owned byte array#17544
Conversation
Review Summary by QodoAdd BinaryMessage.wrap for zero-copy byte array ownership transfer
WalkthroughsDescription• Add BinaryMessage.wrap() factory for zero-copy ownership transfer • Eliminates redundant array copying in fragmented message reassemblers • Updates MessageInboundConverter and WebSocketFrameProxy to use new factory • Adds unit test verifying wrap's no-copy behavior vs constructor's defensive copy Diagramflowchart LR
A["Fragmented Binary Message"] -->|reassemble| B["ByteArrayOutputStream.toByteArray()"]
B -->|old: copy| C["BinaryMessage constructor"]
B -->|new: wrap| D["BinaryMessage.wrap()"]
C --> E["BinaryMessage with copied data"]
D --> F["BinaryMessage with transferred ownership"]
E -->|2 allocations| G["Performance cost"]
F -->|1 allocation| H["Optimized path"]
File Changes1. java/src/org/openqa/selenium/remote/http/BinaryMessage.java
|
Code Review by Qodo
1. Unused ownership parameter
|
ee31f19 to
2c15d04
Compare
|
Persistent review updated to latest commit 2c15d04 |
|
Addressed in 2c15d04: the private constructor now calls |
BinaryMessage's two public constructors both copy their input defensively: the byte[] constructor calls System.arraycopy, and the ByteBuffer constructor copies into a freshly allocated array. That is the right default when the caller cannot promise ownership, but several call sites already produce a fresh array via ByteArrayOutputStream.toByteArray() and then immediately have it copied a second time by the constructor. For a multi-megabyte payload the second copy is wasted work and a wasted allocation. Add a static BinaryMessage.wrap(byte[]) factory that takes ownership of the array without copying. Its javadoc explicitly states that the caller must not mutate the array afterwards. The two public constructors keep their copy semantics, so existing callers that pass in a borrowed array are unaffected. Use the new factory at two slow-path reassemblers that already produce a fresh array we own: - MessageInboundConverter, when a fragmented binary message finishes via buffer.toByteArray() before being forwarded to WebSocketMessageHandler. - WebSocketFrameProxy, when the inbound frame proxy reassembles a fragmented message for an upstream WebSocket that does not support per-frame sends. The hot path through the Router and Node proxies no longer touches BinaryMessage at all — frames travel end-to-end as Netty WebSocketFrame objects — so the saving here is on the fallback paths that handle fragmented binary messages. A focused unit test pins both the copying behaviour of the existing constructor and the no-copy behaviour of the new factory.
2c15d04 to
87c8eec
Compare
|
Code review by qodo was updated up to the latest commit 87c8eec |
Summary
BinaryMessage's two public constructors both copy their input defensively: thebyte[]constructor callsSystem.arraycopy, and theByteBufferconstructor copies into a freshly allocated array. That is the right default when the caller cannot promise ownership, but several call sites already produce a fresh array viaByteArrayOutputStream.toByteArray()and then immediately have it copied a second time by the constructor. For a multi-megabyte payload the second copy is wasted work and a wasted allocation.Add a static
BinaryMessage.wrap(byte[])factory that takes ownership of the array without copying. Its javadoc explicitly states that the caller must not mutate the array afterwards. The two public constructors keep their copy semantics, so existing callers that pass in a borrowed array are unaffected.Use the new factory at two slow-path reassemblers that already produce a fresh array we own:
MessageInboundConverter, when a fragmented binary message finishes viabuffer.toByteArray()before being forwarded toWebSocketMessageHandler.WebSocketFrameProxy, when the inbound frame proxy reassembles a fragmented message for an upstream WebSocket that does not support per-frame sends.The hot path through the Router (#17435) and Node proxies no longer touches
BinaryMessageat all — frames travel end-to-end as NettyWebSocketFrameobjects — so the saving here is on the fallback paths that handle fragmented binary messages.🤖 Generated with Claude Code