michele-tramonti opened a new pull request, #2542:
URL: https://github.com/apache/plc4x/pull/2542

   ## Summary
   
   Fixes #2248 — direct memory leak in the S7 driver that exhausts 
`MaxDirectMemorySize` on long-running applications (`OutOfMemoryError: Cannot 
reserve N bytes of direct buffer memory`).
   
   `S7HMuxImpl` is installed on three pipelines (`EmbeddedChannel` + 
primary/secondary TCP), and its `encode()` was both forwarding the outbound 
message to the active TCP channel and pushing a freshly-copied direct `ByteBuf` 
into the `MessageToMessageCodec` out list. That second copy ends up in the 
`EmbeddedChannel`'s outbound queue, which nothing in plc4j ever drains. Result: 
every sent S7 message leaked one direct buffer, leading to the OOM described in 
the issue (1-2 weeks to reproduce in production).
   
   The fix replaces the second copy with the `Unpooled.EMPTY_BUFFER` singleton 
— Netty still requires at least one element in the out list (otherwise 
`MessageToMessageEncoder` throws `EncoderException`), but the singleton 
allocates zero direct memory and its `release()` is a no-op. The forward to the 
active TCP channel (`outBB.copy()` + `writeAndFlush`) is unchanged, so wire 
behaviour is identical.
   
   ## Why `copy()` and not `retainedDuplicate()` for the TCP forward
   
   `copy()` is kept on purpose: future contributors who modify `S7HMuxImpl` 
(e.g. add another consumer, change the failover logic, insert handlers in the 
TCP pipeline) shouldn't have to reason about shared refcounts and concurrent 
reads of the same backing memory. The cost of one `copy()` per S7 request is 
negligible compared to the safety margin.
   
   ## Regression test
   
   `S7HMuxLeakTest` wires `S7HMuxImpl` exactly like `S7HPlcConnection` does 
(one `EmbeddedChannel` as the logical pipeline, one as the primary TCP 
channel), runs 2000 outbound writes and asserts:
   
   1. each message is forwarded exactly once to the TCP side,
   2. the `EmbeddedChannel`'s outbound queue does not accumulate non-empty 
`ByteBuf`s.
   
   Verified locally: the test fails on the unfixed code with `expected: <0> but 
was: <2000>` (~512 KB of leaked buffers, exactly matching the synthetic 
payload), and passes with the fix.
   
   ## Test plan
   
   - [x] `mvn -pl :plc4j-driver-s7 -P with-java test -Dtest=S7HMuxLeakTest` 
passes with the fix
   - [x] Same test fails deterministically on the unfixed code path (confirms 
the regression test is meaningful)
   - [ ] Long-running soak test against a real S7 PLC by users that hit the 
original issue


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to