[
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)