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

   # Description
   
   [CAMEL-26642](https://issues.apache.org/jira/browse/CAMEL-26642)
   
   With a recoverable aggregation repository (`useRecovery`), a stop + start of 
the route while a recover pass was running could recover and send the same 
exchange twice.
   
   `AggregateProcessor.doStop` shuts down the executor of the recover task but 
does not wait for a pass that is running (no change there). A pass checks 
`isRunAllowed()` before each exchange, so a pass that is in one long call 
during the stop (the `scan` of the repository, the `recover` load of an 
exchange, a dead letter send) went on after a quick start, next to the recover 
task of the new start. The two passes shared the snapshot of the exchanges in 
progress and the `recoveryInProgress` flag (each pass clears them when it 
ends), and they no longer excluded each other because `doStart` creates a new 
aggregation lock:
   
   ```
   old pass:  scan() ...
   admin:     stopRoute, startRoute            (the stop does not wait for the 
old pass)
   new pass:  snapshot = {}, scan() ...
   old pass:  scan -> {Z}, recover Z and send it; ends: recoveryInProgress = 
false, snapshot cleared
   new pass:  scan -> {Z} (not confirmed yet), snapshot empty -> recover Z and 
send it again
   ```
   
   or, with the old pass in `recover(Z)` across the restart, the new pass sends 
Z and then the old pass sends the copy it loaded. On that path the old pass 
also unlocks the lock of the new start (`finally lock.unlock()` reads the field 
again), which throws an `IllegalMonitorStateException`.
   
   Without a restart the passes never overlap (fixed delay on one thread), so 
the in-progress check of CAMEL-24943 holds.
   
   Found while checking the processors that start their own background tasks in 
`doStart` for work that goes on after a quick stop and start (same family as 
CAMEL-26634).
   
   Change (`AggregateProcessor.RecoverTask`):
   - one recover pass at a time: a pass starts only if no other pass runs 
(`recoveryInProgress.compareAndSet` instead of `set`), so the task of the new 
start skips while an old pass is still running. The old pass then is the only 
one, as one long pass is without a restart;
   - a pass unlocks the lock it locked.
   
   Waiting in `doStop` for the pass was not chosen: a pass can be stuck in a 
slow repository call, and the stop would wait for it. A recover pass blocked in 
the repository now also delays the recover task of the next start until it 
returns (before, the two could run together and send an exchange twice).
   
   Tests: `AggregateRecoverRestartTest` (an in-memory recoverable repository 
whose `scan` or `recover` can be held with latches; no sleeps; the tests wait 
for one run of the recover task of the new start through a recording 
`ThreadPoolFactory`, so they hold whether that task recovers or skips):
   - the old pass held in `scan` across the restart, the new pass held in 
`scan` until the old pass ended;
   - the old pass held in `recover` across the restart.
   
   On main both fail (the exchange is sent twice); with the fix 2/2, and 20 
runs each (40/40). Related camel-core tests (the aggregator package, 
`*Aggregat*`, `*Recover*`, `*Suspend*`, `*Shutdown*`): 375 (7 skipped), 0 
failures.
   
   The same `RecoverTask` code is on `camel-4.18.x` and `camel-4.14.x`; the 
change and the test should cherry-pick to them if a backport is wanted.
   
   # 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): 
[CAMEL-26642](https://issues.apache.org/jira/browse/CAMEL-26642).
   
   # 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 the core modules and camel-core with the formatter and 
import-sort plugins and ran the related camel-core tests; no generated file 
changes. 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