CAMEL-25010: camel-core - Supervising route controller: do not start a route stopped during its restart attempt - #26868
Open
allthingssecurity wants to merge 1 commit into
Conversation
…a route stopped during its restart attempt The restart attempt of the supervising route controller only checked CamelContext.isRunAllowed() and then called doStartRoute, which takes the controller lock and starts the route. stopRoute takes the same lock and cancels the route's restart task, but cancelling only prevents future attempts. An attempt that was already running, for example waiting for the controller lock while another route was being stopped, started the route after stopRoute had returned. The route was then running without supervision, and nothing reported it. The attempt now checks, while holding the controller lock, that its task is still the route's restart task and still active, and that Camel is not stopping. Otherwise it gives up without starting the route. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
gnodet-bot
approved these changes
Sep 25, 2026
gnodet-bot
left a comment
Contributor
There was a problem hiding this comment.
Solid concurrency fix. The race between the restart attempt and a concurrent stopRoute is correctly closed by acquiring the controller lock before the guard check and holding it through doStartRoute. Three guard conditions (routes.get(r) != context, task status, isRunAllowed) are necessary and sufficient. Reentrant lock prevents deadlock with doStartRoute's own lock acquisition. Event notification correctly stays outside the lock to avoid lock-inversion with listeners. The test faithfully reproduces the race via RouteRestartingEvent interception with latches — no timing fragility.
This review was generated by an AI agent, Hermès on behalf of @gnodet.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
CAMEL-25010
The restart attempt of
DefaultSupervisingRouteControlleronly checkedCamelContext.isRunAllowed()and then calleddoStartRoute, which takes the controller lock and starts the route.stopRoutetakes the same lock and cancels the route's restart task (RouteManager.release). Cancelling only prevents future attempts:BackOffTimerTask.run()checks the status once, on entry.So an attempt that was already running started the route after
stopRoutehad returned. This happens whenever the attempt waits for the controller lock during the stop, and the lock is shared by all routes, so a slow stop of any other route is enough. The route then ran without supervision (it has no restart task any more), and nothing reported it.This change: the attempt now checks under the controller lock, before it starts the route, that:
routes.get(r) == task);If any check fails, it logs that the attempt was cancelled and gives up without starting the route.
RouteManager.startruns under the same lock, so a new task is always registered before its first attempt can check.Tests: new
DefaultSupervisingRouteControllerStopWhileRestartingTest. A route fails to start. Its first restart attempt is held in theRouteRestartingEventnotifier while the cause is fixed and the route is stopped manually. The attempt is then released, and the route must stay stopped. Latches and Awaitility, no sleeps. Without the fix:With the fix it passes.
*BackOff*,*Supervising*,*RouteController*in camel-util, camel-core and camel-management: all pass.Found with a TLA+ model of the supervising route controller, then reproduced against the real classes, also without holding any thread: route r1 waits to restart while stopping route r2 holds the controller lock (3 of 3 runs restarted r1 after
stopRoute("r1")returned). With only this change, "no restart after the user stopped the route" holds in the model with up to 3 user operations and 3 restart tasks (36k states). The same harness scenarios now leave the route stopped.Related:
RouteManager.release(). This change is in the attempt ofRouteManager.start(). The two apply together without conflicts.hasUnhealthyRoutes()stay wrong until CAMEL-25008, CAMEL-25007: camel-core - Supervising route controller: fix a deadlock and a lost restart task #26867 is merged. A cancelled attempt that gives up here also completes its task, and CAMEL-25008, CAMEL-25007: camel-core - Supervising route controller: fix a deadlock and a lost restart task #26867'sroutes.remove(r, task)makes sure that doesn't remove a newer task. The two PRs touch different parts of the file and merge without conflicts.Target
mainbranch)Tracking
Apache Camel coding standards and style
mvn clean install -DskipTestslocally 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
Co-authored-bytrailers) 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-Bytrailer.Claude Code on behalf of allthingssecurity
🤖 Generated with Claude Code