shashank created CAMEL-26048:
--------------------------------

             Summary: camel-mina - every route restart leaks the acceptor of 
the stopped consumer (a non-daemon thread and its selectors)
                 Key: CAMEL-26048
                 URL: https://issues.apache.org/jira/browse/CAMEL-26048
             Project: Camel
          Issue Type: Bug
          Components: camel-mina
            Reporter: shashank
            Assignee: shashank


{{MinaConsumer}} builds its acceptor (or, in client mode, its connector) and 
its worker pool in the constructor. {{doStop}} only unbinds the address (client 
mode: disposes the connector); {{acceptor.dispose(true)}} and 
{{workerPool.shutdown()}} are only in {{doShutdown}} (main 0c1e85c8631c). A 
route that is stopped and started again does not shut its consumer down: 
{{RouteService}} stops it and the next start creates a new consumer, so the old 
consumer's {{doShutdown}} never runs. Each route restart leaks the stopped 
acceptor: tcp a {{NioSocketAcceptor}} with its acceptor thread (not a daemon 
thread) and one selector per I/O processor ({{availableProcessors() + 1}}) plus 
the worker pool; udp a {{NioDatagramAcceptor}} and its worker pool; vm a 
{{VmPipeAcceptor}} and its idle status checker thread (not a daemon thread). 
Measured on a 10-core machine, 5 restarts of a tcp consumer: 5 more 
{{NioSocketAcceptor-N}} threads and about 210 more open file descriptors, never 
released; the non-daemon threads also keep the JVM running after the 
CamelContext is stopped. Triggers: route stop/start (JMX, control bus, dev 
console, RouteController), route policies that stop and start their route, and 
the start retries of the {{SupervisingRouteController}} (each failed attempt 
creates a new consumer). CAMEL-9738 added the dispose to {{doShutdown}}, which 
this path never reaches. Removing a route (route reload, route templates and 
kamelets created again) shuts the current consumer down, so there only 
consumers left over from earlier restarts leak.

*Fix*: {{doStop}} disposes the acceptor after the unbind and shuts the worker 
pool down, also in client mode. It waits for the I/O threads 
({{dispose(true)}}) only when the CamelContext is stopping, so 
{{CamelContext.stop()}} still waits for them as before; a route stop does not 
wait (the address is already unbound). Either way the acceptor is marked 
disposed, so the {{dispose(true)}} of {{doShutdown}} then does nothing (javap 
{{AbstractIoService.dispose}}, mina-core 2.2.9). The protocol setup runs again 
in {{doStart}} when the consumer was stopped, so a consumer that is stopped and 
started again (route or CamelContext suspend/resume, a route policy that stops 
the consumer itself) gets a new acceptor or connector; before, it bound the old 
acceptor again, and in client mode the start failed with 
{{IllegalStateException: The connector is being disposed.}}

*Tests*: new {{MinaConsumerRestartTest}}, 6 tests (tcp, udp and vm route 
restarts: every stopped acceptor disposed, the tcp and udp worker pools shut 
down, the restarted route receives; same-instance stop/start, also in client 
mode; a route stop calls {{dispose(false)}} and a CamelContext stop 
{{dispose(true)}}), all fail on main, for example {{the acceptor of a stopped 
consumer must be disposed ==> expected: <true> but was: <false>}}, and in 
client mode {{IllegalStateException: The connector is being disposed.}}. Unit 
tests of camel-mina with the fix: 122 run, 0 failures, 4 skipped.

*How it was found*: a per-class thread/fd leak check over module test suites 
and a route restart probe (r75).

*Affected*: main, camel-4.23.x, camel-4.22.x, camel-4.18.x and camel-4.14.x 
(same consumer). Read, not run.

_Filed with Claude Code on behalf of allthingssecurity._



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

Reply via email to