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]

Reply via email to