allthingssecurity opened a new pull request, #26861: URL: https://github.com/apache/camel/pull/26861
# Description [CAMEL-25003](https://issues.apache.org/jira/browse/CAMEL-25003) For endpoints whose producer is not a singleton (camel-ftp/sftp/ftps, camel-smb, camel-ssh, and the polling consumers of pollEnrich), `ServicePool` keeps a queue of idle producers per endpoint and an LRU cache of the producers it handed out. When the LRU evicted such a producer, `onEvict` only added it to the pool's `evicts` list, and the next acquire or release stopped it. It did not take the producer out of the queue, so: - an idle evicted producer was stopped and then handed out by the next `acquire`, stopped; - a producer that was in use when it was evicted was stopped under the running exchange, and on release it went back to the queue and was handed out again, stopped. With `toD("pooled:${header.to}", 2)`, 4 threads and 2000 exchanges to two endpoints, 1664 exchanges were sent with a stopped producer. This change (in `ServicePool.MultiplePool`): - keeps the set of its producers that were not evicted; - `evict` takes an idle producer out of the queue before `cleanupEvicts` stops it, so it cannot be acquired again. A producer in use is left alone; - `release` stops a producer that was evicted while in use, instead of offering it back to the queue; - `stop()` also stops the pending evicts, since they are no longer in the queue; - `onEvict` no longer stops a non-singleton producer whose pool is gone. At that point the producer can only be in use, and `release` stops it (CAMEL-24248). Singleton producers are still stopped there, as before. - as `onEvict` no longer stops those producers, the pool marks itself stopped: a producer released, or evicted, into a pool while it is being stopped is stopped there, and is not left behind in the queue or the evicts of the stopped pool. Stopping still happens on acquire/release, not in the LRU eviction callback, as the `onEvict` javadoc describes. `evict` now calls `queue.remove(s)`, which scans the idle queue of that endpoint only. The old code did the same in `cleanupEvicts` before CAMEL-20829. Handing out a stopped producer is a regression from CAMEL-20829 (4.7), which dropped `queue.remove(evict)` from `cleanupEvicts`. Tests: new `ProducerCacheNonSingletonEvictionTest` (single thread, deterministic, uses `cache.cleanUp()` to force the eviction): - `testEvictIdleProducer`: the next acquire returns a started producer, not the evicted one. - `testEvictProducerInUse`: the evicted producer is not stopped while in use. It is stopped when released and is not handed out again. Without the fix both fail: ``` testEvictIdleProducer AssertionFailedError: Acquired producer should be started ==> expected: <true> but was: <false> testEvictProducerInUse AssertionFailedError: Evicted producer should not be stopped while it is in use ==> expected: <true> but was: <false> ``` With the fix both pass. `*ProducerCache*,*ServicePool*,*Pool*,*ConsumerCache*,PollEnrich*,*Evict*,ToD*,RecipientList*,Enricher*,*LRUCache*,*SoftCache*`: 312 tests, 0 failures (including `DefaultProducerCacheTest` and `PollEnrichConcurrentConsumersCacheTest` from CAMEL-24248). Found with a TLA+ model of `ServicePool` (pooled producers, LRU eviction, concurrent acquire/release), then reproduced against the real classes. The model of this change satisfies all five invariants (never used after stop, never stopped in use, the queue holds only started producers, never shared, no leak) with cache size 2 and 1, up to 362k states. The race between the membership check and the offer in `release` is modelled as a separate step. The stopped-pool guard was added after the model run and is not part of the model. The route scenario above now gives `usedAfterStop=0 stopWhileInUse=0`, and every producer is stopped when the context stops. # 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 modules, 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]
