Henrik created CAMEL-24584:
------------------------------
Summary: camel-support: BackgroundTask.schedule keeps re-running a
task that has completed or run out of budget
Key: CAMEL-24584
URL: https://issues.apache.org/jira/browse/CAMEL-24584
Project: Camel
Issue Type: Bug
Components: camel-core
Affects Versions: 4.22.0, 4.21.0, 4.20.0, 4.18.3
Reporter: Henrik
{{BackgroundTask.schedule(CamelContext, BooleanSupplier)}} submits the task with
{{scheduleWithFixedDelay}} and returns the {{Future}}, but the task never
cancels it. Once the task is finished, {{runTaskWrapper}} short-circuits on its
own latch:
{code:java}
if (latch.getCount() == 0) {
return;
}
{code}
so the runnable keeps being re-run at the task interval, doing nothing, for the
life of the executor. This happens on both terminal paths:
* the supplier returned {{true}}, so the task is {{Completed}};
* the budget ran out, so the task is {{Exhausted}}.
Cancelling is left to every caller. {{camel-sjms}}
({{SimpleMessageListenerContainer}}) does cancel the future it keeps;
{{camel-master}} did not, which is one of the observations in CAMEL-24583 -
every leadership change left a repeating no-op task on the "Leadership" pool
for the life of the route.
The {{Exhausted}} case cannot be fixed by the caller at all. After the budget
runs out the supplier is never invoked again, so nothing component-side is ever
given the chance to notice and cancel.
h2. Proposed fix
Record the future inside {{schedule()}} and cancel it on the paths where the
task can no longer do any work: {{Completed}}, {{Exhausted}}, and a guard for
the case where a task with a zero initial delay finishes before the future has
been published.
The blocking {{run()}} path is deliberately left untouched: it already cancels
the future it owns in {{waitForTaskCompletion}}, and {{camel-sjms}} branches on
{{isRunning()}}, which that path controls.
h2. Compatibility
{{schedule()}} is public and is called from outside core. A caller that
inspects the returned {{Future}} will now see {{isCancelled()}} return {{true}}
once the task is done, where it previously stayed live forever. Callers that
already cancel the future themselves are unaffected. An upgrade guide entry is
included.
h2. Not addressed here
When a *caller* cancels the future instead, the task is never removed from the
{{TaskManagerRegistry}} - {{runTaskWrapper}} only unregisters from inside a
later run, which by then cannot happen. The task stays listed as {{Active}} in
JMX and in the {{camel/internal-tasks}} dev console until the context stops.
That affects {{camel-sjms}} today and {{camel-master}} after CAMEL-24583, and
would be better fixed by giving {{BackgroundTask}} a cancel operation that also
deregisters. Worth a separate issue.
h2. References
The behaviour dates back to CAMEL-22206, which introduced {{schedule()}} in
4.13.0. Related recent work in the same class: CAMEL-24278 and CAMEL-24286.
The change is already included in the pull request for CAMEL-24583:
https://github.com/apache/camel/pull/26028
--
This message was sent by Atlassian Jira
(v8.20.10#820010)