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

   # Description
   
   [CAMEL-26104](https://issues.apache.org/jira/browse/CAMEL-26104)
   
   `ThrottlingExceptionRoutePolicy.addHalfOpenTimer` created a new 
`java.util.Timer`, and with it a new thread (`Timer-N`), every time the circuit 
opened or was kept open. These threads are not named or managed by Camel, the 
timers of a removed route kept running (with `keepOpen` or a `halfOpenHandler` 
that is not ready, each check scheduled the next one forever), and in 
camel-test-dst the check fired after `halfOpenAfter` of real time on a thread 
outside the harness, so a DST world could not see the circuit half open in 
virtual time (test-dst.adoc listed it as an escape).
   
   Changes (`ThrottlingExceptionRoutePolicy`, camel-support):
   - The half open check runs on a single thread scheduled executor that the 
policy creates through the `ExecutorServiceManager` the first time it schedules 
a check (`newSingleThreadScheduledExecutor(this, 
"ThrottlingExceptionRoutePolicy")`, so the thread is named `Camel (<context>) 
thread #N - ThrottlingExceptionRoutePolicy` and the pool is managed like the 
other thread pools). The same thread runs all later checks of the policy.
   - The semantics of CAMEL-26291 (#28467) stay as they are: one check at a 
time (scheduling a check cancels the one scheduled before, with 
`cancel(false)`, so a check that is running is not interrupted, like 
`Timer.cancel()`), and a check that finds the circuit already half opened by an 
exchange leaves it to the next exchange. `calculateState` and the state 
handling are unchanged.
   - CAMEL-26313 (#28574) is kept as it is: `addHalfOpenTimer(Route)` calls the 
protected `addHalfOpenTimer(Route, long delay)`, and that overload now 
schedules on the scheduler with its `delay`. So a check that finds 
`halfOpenAfter` raised while the circuit was open schedules the next check for 
the time that is left, on the same thread, and an exchange that is done while 
the circuit is open still schedules nothing. A subclass that overrides 
`addHalfOpenTimer(Route, long)` (as 
`ThrottlingExceptionRoutePolicyInflightWhileOpenTest` does) sees every check, 
as on main.
   - The policy is a service of the CamelContext, so removing its route does 
not stop it. `onRemove` now shuts the scheduler down when the last route of the 
policy is removed (the policy tracks the ids of its routes from `onInit`), and 
`doStop` shuts it down when the policy stops. A policy shared by two routes 
keeps its scheduler while one of them is left. A pending check is cancelled; a 
check that is running at that moment finishes, and it does not create a new 
scheduler (a flag, cleared again by `onInit`, `onStart` and `doStart`, so a 
route added again or a restarted context get checks again).
   - The scheduler and its flag have their own lock (`schedulerLock`), not the 
`lock` that a running check holds while it suspends or resumes the consumer, so 
`onRemove` and `doStop` do not wait for a running check.
   - `onInit` sets the CamelContext from the route when it was not injected.
   - A negative `halfOpenAfter` made `Timer.schedule` throw on main (the 
circuit stayed open and no check ran); the scheduler runs that check at once, 
as it does for 0.
   
   Docs:
   - test-dst.adoc (and its catalog copy): the half-open check is no longer 
listed as a wait outside virtual time, and the escape bullet is removed.
   - Upgrade guide 4.24: its own section, `camel-core - 
ThrottlingExceptionRoutePolicy runs the half open check on a thread pool of 
Camel`, after the sections main added since (the thread name and the thread 
pool are visible in thread dumps and JMX).
   
   Tests:
   - New `ThrottlingExceptionRoutePolicyHalfOpenSchedulerTest` (camel-core, 3 
tests, latches and Awaitility, no sleeps):
     - `testHalfOpenChecksRunOnOneThreadOfThePolicy`: two openings, both checks 
half open the circuit on the same thread, named by the 
`ExecutorServiceManager`. Fails on main (two different `Timer-N` threads).
     - `testRemovingTheRouteEndsTheThreadOfTheHalfOpenCheck`: with a check 
pending (`halfOpenAfter` 60 s), stop and remove the route; the scheduler thread 
ends and the check does not run. Fails on main (no thread of the policy's 
scheduler).
     - `testRemovingOneOfTwoRoutesOfAPolicyKeepsItsHalfOpenCheck`: a policy on 
two routes, the circuit opened by the second; removing the first keeps the 
pending check, which then half opens the circuit; removing the second ends the 
thread. A control: it passes on main, and fails when `onRemove` shuts the 
scheduler down on every removal.
   - `ThrottlingExceptionRoutePolicyFailureWindowTest` (CAMEL-26291): the race 
test gated the check by the thread name `Timer-` and then joined the timer 
thread. It now gates on the new thread name, and waits for the end of the check 
by submitting an empty task to the policy's scheduler (one thread, so it runs 
after the check; the test captures the scheduler with a 
`DefaultThreadPoolFactory` subclass). The assertions are unchanged.
   - New `DstHalfOpenCheckTest` (camel-test-dst, strict mode, 2 tests):
     - `theCircuitIsHalfOpenedAfterHalfOpenAfterOfVirtualTime`: a failing 
exchange at 1 s opens the circuit (`halfOpenAfter` 30 s); after 30 s of virtual 
time it is still open, and the run then half opens it at exactly 31 000 ms, the 
scheduler shows in the trace as `thread pool created: 
ThrottlingExceptionRoutePolicy (1 threads)`, in well under a second of real 
time.
     - `aRaisedHalfOpenAfterHalfOpensTheCircuitWhenTheNewTimeIsUp` (with 
CAMEL-26313): the same opening at 1 s, and `halfOpenAfter` raised to 40 s at 20 
s; the check at 31 s finds the time not up and schedules the next one for the 
10 s that are left, so the circuit is half opened at exactly 41 000 ms. It 
fails when `addHalfOpenTimer(Route, long)` schedules `halfOpenAfter` instead of 
its `delay` (half opened at 71 000 ms) and when the check does not schedule 
again.
     - Both fail on main ("nothing can run any more while waiting for: the 
circuit is half opened": the `Timer` is outside the run).
   - On current main (`*Throttl*` test classes and all of camel-test-dst, one 
Maven run, `forkCount=1`): camel-core 76/0 (including main's 
`ThrottlingExceptionRoutePolicyRaiseHalfOpenAfterTest` and 
`ThrottlingExceptionRoutePolicyInflightWhileOpenTest`), camel-support 3/0, 
camel-test-dst 59/0. In camel-management, 10 of 12 passed; 
`ManagedThrottlingExceptionRoutePolicyTest` and 
`ManagedThrottlingInflightRoutePolicyTest` failed in that run (`expected: <1> 
but was: <8>`: they count the route MBeans of the whole JVM and saw those of 
the other throttler test classes). They fail the same way with main's 
`ThrottlingExceptionRoutePolicy` in the same set of test classes, and both pass 
on their own. The formatter and import-sort plugins ran and changed nothing.
   - `ThrottlingExceptionRoutePolicyHalfOpenSchedulerTest`, 
`ThrottlingExceptionRoutePolicyFailureWindowTest` and `DstHalfOpenCheckTest`: 
20 runs each in one JVM, no failure (60, 60 and 40 tests).
   
   Merge note: the upgrade guide section is added after the last 
camel-core/camel-util section, where the queued Failover load balancer change 
(CAMEL-26328) adds its section too; whichever merges second has a conflict of 
two adjacent insertions.
   
   Backport: builds on CAMEL-26291 and the `TimeSource` of CAMEL-25911, both 
only on main, so main only.
   
   # 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-26104) 
filed for the change (usually before you start working on it): 
[CAMEL-26104](https://issues.apache.org/jira/browse/CAMEL-26104).
   
   # 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, the components on the camel-core test 
classpath, camel-management and camel-test-dst and ran the tests above, 
including the formatter and import-sort plugins. The test-dst.adoc change is 
copied to its catalog copy. 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