shashank created CAMEL-25849:
--------------------------------

             Summary: camel-disruptor - a disruptor-vm consumer stops consuming 
for good when the CamelContext that first used the queue is stopped
                 Key: CAMEL-25849
                 URL: https://issues.apache.org/jira/browse/CAMEL-25849
             Project: Camel
          Issue Type: Bug
          Components: camel-disruptor
            Reporter: shashank
            Assignee: shashank


A {{disruptor-vm}} queue is shared by all CamelContexts of the JVM 
({{DisruptorVmComponent.DISRUPTORS}} is static), but the threads that run its 
event handlers are always created by the {{ExecutorServiceManager}} of the 
CamelContext whose component created the queue: 
{{DisruptorReference.resizeThreadPoolExecutor}} (main 20cdb309f7d0, lines 
259-291) uses {{component.getCamelContext().getExecutorServiceManager()}}, and 
{{component}} is the component that first resolved the queue name.

When that CamelContext stops while another CamelContext still consumes from the 
queue, the stopping CamelContext shuts the thread pool down as part of its own 
shutdown ({{BaseExecutorServiceManager}}: "Forcing shutdown of ExecutorService 
... [disruptor-vm://foo] due first await termination elapsed", after 10 
seconds, then "Forced shutdown of 1 ExecutorService's which has not been 
shutdown properly (acting as fail-safe)"). The queue itself stays registered, 
as the other CamelContext still has endpoints on it. From then on the consumer 
of the other CamelContext never consumes again: its route stays Started, 
nothing is logged in that CamelContext, and every message sent to the queue 
(from any CamelContext) stays in the ring buffer as an inflight exchange. When 
the ring buffer is full, producers block ({{blockWhenFull}}) or fail; when the 
consuming CamelContext stops, it waits the whole shutdown timeout for those 
inflight exchanges and then drops them.

A typical case: CamelContext A has 
{{from("direct:in").to("disruptor-vm:orders")}} and is created first, 
CamelContext B has {{from("disruptor-vm:orders")}}. A is stopped or redeployed: 
B's consumer is dead for good. The existing test 
{{DisruptorVmMultipleContextsStartStopTest}} covers a restart of the consuming 
CamelContext, not a stop of the one that created the queue.

*Fix*: {{DisruptorReference}} creates the thread pool with the CamelContext of 
an endpoint that uses the queue, and remembers it. When the last endpoint of 
that CamelContext is removed (the endpoints are stopped before the 
{{ExecutorServiceManager}}) while endpoints of another CamelContext remain, it 
moves the event handlers to a new thread pool of one of those CamelContexts, 
the same way a reconfiguration does (the backlog is kept and published again). 
With a single CamelContext nothing changes. No option changes.

*Tests*: new {{DisruptorVmFirstContextStopTest}}: CamelContext 1 sends to 
{{disruptor-vm:foo}} (it creates the queue), CamelContext 2 consumes from it 
and also has a route that sends to it. After CamelContext 1 is stopped, a 
message sent from CamelContext 2 must reach CamelContext 2's consumer. On main 
it fails ({{mock://result Received message count. Expected: <1> but was: <0>}}; 
the stop of CamelContext 1 takes 10 s and the stop of CamelContext 2 waits 45 s 
for the stuck exchange); with the fix it passes in 0.2 s. camel-disruptor unit 
tests with the fix: 118 run, 0 failures.

*How it was found*: variant analysis of CAMEL-25846 (an object kept in a 
static, JVM-wide map that depends on a lifecycle owned by one instance), then 
the JUnit test. Not run in an application server with several deployments.

*Affected*: main, camel-4.23.x, camel-4.22.x and camel-4.18.x (the same 
executor creation in {{DisruptorReference}}; read, not run).

_Filed with Claude Code on behalf of allthingssecurity._




--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to