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

   # Description
   
   [CAMEL-25612](https://issues.apache.org/jira/browse/CAMEL-25612)
   
   `SimpleMessageListenerContainer.initConsumers` assigned the sets of sessions 
and consumers before it filled them. When `createSession` or 
`createMessageConsumer` threw, the sets stayed, empty or half filled.
   
   During connection recovery that was permanent:
   
   1. attempt n creates the new connection, assigns the sets, one create call 
fails, the attempt fails (WARN) and is repeated after `recoveryInterval`;
   2. attempt n+1 closes that connection (and the sessions of attempt n with 
it), creates another one, finds `consumers != null`, creates nothing, and 
reports success.
   
   The route stayed started with a started connection and no consumer. Nothing 
above DEBUG was logged after the WARN of attempt n, no further recovery was 
scheduled, and no message was consumed until the route or the context was 
restarted. The reply listeners of the InOut producer use the same container.
   
   A create call fails on a fresh connection when the connection fails again 
while the consumers are created (broker still restarting, failover) and 
`onException` of that failure arrives before `initConsumers` assigns the sets, 
or when the connection is fine and the destination or the permission is not 
there yet (then no `onException` follows, and every such failure ends this way).
   
   Changes, all in `SimpleMessageListenerContainer`:
   
   - `initConsumers` closes what it created and sets the two fields back to 
`null` when the loop fails, so the next attempt creates all the consumers.
   - The same `catch` block closes the session whose consumer could not be 
created when the container fails to start. That session stayed open on the 
connection, which is kept for the next start.
   - `recoverConnection` runs another attempt (it returns `false`, so the task 
runs again after `recoveryInterval`) when `onException` dropped the consumers 
while the attempt was creating them. `onException` does not schedule a recovery 
while one is running. On main this case mostly worked by accident: the loop 
added to the fields that `onException` had set to `null`, and the 
`NullPointerException` failed the attempt. The loop no longer fails that way, 
so the check is needed. Only an `onException` after the last pass of the loop 
was lost on main; that window is closed too.
   
   No new retry loop and no new log per attempt: the attempts are those of the 
existing recovery task, one WARN line without a stack trace per failed attempt 
as before; the new case logs at DEBUG.
   
   No upgrade guide entry: nothing a route can rely on changes.
   
   Tests:
   - New `SjmsConsumerRecoveryFailedAttemptTest` (4 tests) with the embedded 
Artemis broker of the module and a connection factory that tracks the 
connections, sessions and consumers of the route and can make one create call 
fail. `concurrentConsumers=3`, `recoveryInterval=200`.
     - After a simulated connection failure the first session (one test) or the 
last session (another test) of the next connection cannot be created. The test 
waits for the attempt after that (the third connection), sends a message and 
expects it, then expects 3 consumers on the third connection.
     - A container that fails to start because its second consumer cannot be 
created leaves no open session and no open consumer; a second start creates 
both consumers on the same connection.
     - `onException` during the creation of the last session of the recovery 
connection: another attempt runs and the consumers are on a third connection.
   - Without the change (main's `SimpleMessageListenerContainer`, same test) 
three of the four fail: `mock://first Received message count. Expected: <1> but 
was: <0>`, `mock://last Received message count. Expected: <1> but was: <0>`, 
and `open sessions after the failed start ==> expected: <0> but was: <1>`. The 
fourth one (`onException` during the creation) passes on main, through the 
`NullPointerException` described above; it is there because the loop no longer 
fails that way.
   - With the change, camel-sjms tests pass: 138 run, 0 failures, 5 skipped.
   
   What was not run: camel-sjms2 (it uses the same container), and no broker 
other than the embedded Artemis. The failing create call is simulated by the 
connection factory of the test.
   
   Notes:
   - The open pull request #27173 (CAMEL-16039) changes `onException` in the 
same file: it moves the three lines that reset the fields into a method. It 
does not touch `initConsumers` or `recoverConnection`, and the two changes 
merge without a conflict today.
   - How it was found: a small TLA+ model of the container (connection alive or 
failed, the pending `onException` call, the `consumers` field, the consumers on 
the current connection, the steps of one recovery attempt). For main it reaches 
a state with a live connection, no consumer and no recovery scheduled, after 
two connection failures or after one connection failure and one failing create 
call. The model is only a sketch of the code; the test above is the evidence.
   
   # Target
   
   - [x] I checked that the commit is targeting the correct branch (Camel 4 
uses the `main` branch)
   
   # Tracking
   - [ ] 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).
     (The ticket is drafted and not filed yet; CAMEL-25612 is a placeholder 
until it is.)
   
   # 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 camel-sjms, 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