[ 
https://issues.apache.org/jira/browse/CAMEL-25680?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Work on CAMEL-25680 started by shashank.
----------------------------------------
> camel-docker - the events and stats consumers never close their stream (a 
> restarted route gets every event twice), and the events consumer stays 
> started without a stream after the Docker daemon restarts
> ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
>
>                 Key: CAMEL-25680
>                 URL: https://issues.apache.org/jira/browse/CAMEL-25680
>             Project: Camel
>          Issue Type: Bug
>          Components: camel-docker
>            Reporter: shashank
>            Assignee: shashank
>            Priority: Major
>
> The {{docker:events}} and {{docker:stats}} consumers start a streaming 
> command with {{exec(callback)}}. docker-java reads the stream on its own 
> thread and the stream belongs to the callback 
> ({{ResultCallbackTemplate.onStart(Closeable)}} keeps it, {{close()}} closes 
> it).
> h3. 1. The stream is never closed
> {{DockerEventsConsumer.doStop()}} and {{DockerStatsConsumer.doStop()}} (main 
> cd2ce899764e, line 80 and 70) call {{close()}} on the command only. In 
> docker-java 3.7.1 {{AbstrDockerCmd.close()}} does nothing (read with javap: 
> the method body is {{return}}), and the callback is not kept anywhere. So 
> after a stop:
> * the HTTP connection of the stream stays open, one per stop (a leak on every 
> route stop or context reload);
> * the old callback keeps calling {{getAsyncProcessor().process(...)}} for 
> every event it receives. When the route is started again, the new consumer 
> opens a second stream, and every Docker event is routed twice; after n 
> restarts, n + 1 times.
> Trigger: default options, any {{stopRoute}} / {{startRoute}} of a route that 
> consumes from {{docker:events}} (route controller, a supervising route 
> controller restarting a route, a route reload).
> h3. 2. A stream that fails or ends is not opened again
> {{DockerEventsConsumer.EventsCallback}} (85-99) implements only {{onNext}}. 
> When the stream fails (the daemon restarts, the connection is lost) 
> docker-java's {{ResultCallbackTemplate.onError}} logs one ERROR ("Error 
> during callback") and closes it; when the daemon ends the stream 
> ({{onComplete}}) nothing is logged. The consumer stays started and never 
> receives an event again, until the route is restarted. The same happens when 
> the daemon cannot be reached when the route starts.
> h3. Reproduction
> New {{DockerEventsConsumerStreamTest}} (mocked {{DockerClient}} that captures 
> the callbacks; the test calls {{onStart}} / {{onNext}} / {{onError}} / 
> {{onComplete}} itself). On main:
> * stop and start the route, then the daemon sends one event to each open 
> stream: {{mock://result Received message count. Expected: <1> but was: <2>}};
> * the stream opens only after the route was stopped: {{the stream opened 
> after the stop was not closed ==> expected: <1> but was: <0>}};
> * {{docker:stats}}: {{stopping the route did not close the stats stream ==> 
> expected: <1> but was: <0>}};
> * {{onError(IOException)}} on the open stream, and {{onComplete()}}: no new 
> {{eventsCmd().exec(...)}} within 10 seconds ({{ConditionTimeout}}).
> h3. Proposed fix
> * Both consumers keep their callback, close it on stop (the callback also 
> closes a stream that only opens after the stop, as {{onStart}} reopens a 
> closed template), and a closed callback routes nothing.
> * The events consumer handles {{onError}} and {{onComplete}}: it subscribes 
> again after 1 second, then with a delay that doubles up to 30 seconds while 
> subscribing fails (the same delays as the kubernetes consumers' 
> {{KubernetesHelper.watchAgain}}). A failed open stream is logged once with 
> its stack trace (WARN), a failed attempt to subscribe again as a WARN line 
> without stack trace, a clean end as a WARN line. The new stream starts with 
> the events from then on: events of the time without a stream are not received 
> (as with the kubernetes watches). The delay runs on a scheduled executor of 
> the consumer, shut down on stop.
> Upgrade guide entry (4.24): the events consumer subscribes again when its 
> stream fails or ends; stopping a consumer closes its stream.
> camel-docker unit tests with the fix: 54 run, 0 failures, 4 skipped (the 
> integration tests need a daemon). Not run: a real Docker daemon (the module's 
> integration tests), a daemon restart.
> How it was found: a sweep of the reconnect code of the components (onError / 
> onComplete missing); while writing the test, the no-op {{close()}} of the 
> command.
> Not changed (follow-ups): the stats stream also ends when the container stops 
> and is not opened again (subscribing again to a stopped container would fail 
> every time, so it needs its own rule); 
> {{withSince(String.valueOf(System.currentTimeMillis()))}} passes milliseconds 
> where the Docker API expects seconds, so the {{CamelDockerInitialRange}} 
> option likely never replays past events (read, not checked against a daemon).
> Affected: main (cd2ce899764e; tested). camel-4.23.x, camel-4.22.x, 
> camel-4.18.x and camel-4.14.x have the same {{doStop}} (only 
> {{eventsCmd.close()}}) and an {{EventsCallback}} with only {{onNext}} (read, 
> not run).
> Duplicate check (2026-10-15): JIRA component camel-docker with text "events" 
> (CAMEL-7834 creation 2014, CAMEL-9671 API 1.22, CAMEL-10304 OSGi, CAMEL-10307 
> upgrade), text "DockerEventsConsumer" (nothing); GitHub pull requests 
> "DockerEventsConsumer", "DockerStatsConsumer", "camel-docker events" (nothing 
> related).
> _Filed with Claude Code on behalf of allthingssecurity._



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

Reply via email to