allthingssecurity opened a new pull request, #27037:
URL: https://github.com/apache/camel/pull/27037

   # Description
   
   [CAMEL-25128](https://issues.apache.org/jira/browse/CAMEL-25128)
   
   With a correlation manager that extends `TimeoutCorrelationManagerSupport` 
(the documented recommendation for custom correlation managers), a request 
whose write fails is completed twice: once by the write listener and once by 
the correlation timeout.
   
   `NettyProducer.processWithConnectedChannel` registers the state with 
`putState` (a `TimeoutMap` entry) and writes the request. Two paths can then 
complete the exchange, and they do not see each other:
   - write fails first (a connection closed or reset by the server, an encoder 
that rejects the message): the listener sets the cause and calls 
`onExceptionCaughtOnce`, which completes the exchange. The `TimeoutMap` entry 
stays, so `timeout` ms later `onEviction` sets an `ExchangeTimedOutException` 
on the same exchange and calls the raw `callback.done(false)` again. No timing 
window is needed for this order.
   - timeout first (a large request still in the outbound buffer): `onEviction` 
completes the exchange without setting the state's "callback called" flag, so 
when the write then fails, `onExceptionCaughtOnce` completes it again.
   
   On a route this runs the exchange's on completions as `onComplete` and then 
`onFailure`, fires `ExchangeCompleted` and then `ExchangeFailed`, and leaves 
the inflight count at -1. The reply path is safe (`getState` removes the entry, 
atomically with the eviction), and so is the default 
`DefaultNettyCamelStateCorrelationManager`, whose completions all run on the 
channel's event loop through `callbackDoneOnce`.
   
   This change:
   - `NettyCamelState.markDone()`: a compare-and-set on the flag that 
`callbackDoneOnce` already uses; `callbackDoneOnce` uses it.
   - `NettyCamelState.onExceptionCaughtOnce(doneSync, cause)`: sets the cause 
(or the generic `IOException`) only after it has claimed the exchange. The 
existing `onExceptionCaughtOnce(doneSync)` is kept and delegates to it.
   - `NettyProducer`: the write listener passes the cause instead of setting it 
on the exchange first, so a completed exchange is not changed.
   - `TimeoutCorrelationManagerSupport.onEviction`: sets the timeout and calls 
the callback only if it claimed the exchange.
   - The correlation entry of a failed write is still removed at the timeout, 
as today; the claim makes that eviction a no-op.
   
   Tests:
   - New `NettyTimeoutCorrelationManagerWriteFailureTest`: a 
`TimeoutCorrelationManagerSupport` subclass (timeout 200 ms) with a worker pool 
that counts the processed timeouts, and an outbound handler that fails the 
write either at once, or when the test fails the pending write after the 
timeout was processed. No sleeps. Each test counts the callbacks of the 
producer and checks the exception on the exchange.
   - Without the change both tests fail: `[IOException: Simulated write 
failure, ExchangeTimedOutException]` and `[ExchangeTimedOutException, 
IOException: Simulated connection reset]`.
   - With the change, all camel-netty tests: 130 tests, 0 failures, 0 errors, 9 
skipped.
   
   The netty hunks of the open #22358 (getOut to getMessage) still apply on top 
of this change.
   
   Found with a TLA+ model of the write, the write listener, the eviction and 
the reply, which finds both orders and holds with the claim. I then reproduced 
both with the real producer against a Netty server on localhost, including the 
negative inflight count on a route.
   
   # Target
   
   - [x] I checked that the commit is targeting the correct branch (Camel 4 
uses the `main` branch)
   
   # Tracking
   - [x] If this is a large change, bug fix, or code improvement, I checked 
there is a [JIRA issue](https://issues.apache.org/jira/browse/CAMEL) filed for 
the change (usually before you start working on it).
   
   # Apache Camel coding standards and style
   
   - [x] I checked that each commit in the pull request has a meaningful 
subject line and body.
   - [ ] I have run `mvn clean install -DskipTests` locally from root folder 
and I have committed all auto-generated changes.
     (I built and tested the affected module, including the formatter and 
import-sort plugins. I did not run the full root build.)
   
   # AI-assisted contributions
   
   - [x] If this PR includes AI-generated code, commits have proper 
co-authorship attribution (e.g., `Co-authored-by` trailers) and the PR 
description identifies the AI tool used.
     This PR was prepared with Claude Code (Claude Opus 5.5). The commit 
carries a `Co-Authored-By` trailer.
   
   _Claude Code on behalf of allthingssecurity_
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)
   


-- 
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