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)

Reply via email to