Serdar Gökay created CAMEL-25823:
------------------------------------
Summary: camel-core - Split and Multicast start their child
producers before CamelContext starts, so with the supervising route controller
a producer that cannot start yet fails the whole context
Key: CAMEL-25823
URL: https://issues.apache.org/jira/browse/CAMEL-25823
Project: Camel
Issue Type: Bug
Components: camel-core
Affects Versions: 4.22.1, 4.23.0
Reporter: Serdar Gökay
With the supervising route controller enabled, a route whose producer cannot
start yet (for example its broker is down) is retried with back-off, and Camel
starts. This works for a {{to}} directly in the route. In a quick check on
4.23.0 Camel also started, and the route ended up Started, with the {{to}}
inside filter, choice, doTry, loop, loadBalance and wireTap, and with enrich
and recipientList (which create their producer at runtime). When the same
{{to}} is inside {{split}} or {{multicast}}, the producer is started during
route initialization, before the CamelContext starts and outside the
supervising controller. The start failure then fails the CamelContext, and the
route is never supervised.
*Reproduction* (plain camel-core unit test, no external system;
SupervisingRouteControllerMulticastProducerStartTest, ready for a PR): a
component whose producer fails its first two {{doStart}} calls with "Cannot
connect", supervising controller enabled (backOffDelay 25, backOffMaxAttempts
10, initialDelay 10).
||Route||Result||
|{{from(direct).to(flaky)}}|Camel starts, route retried and Started after 2
failed starts|
|{{from(direct).split(body()).to(flaky)}}|{{context.start()}} throws
{{IllegalStateException: Cannot connect}}|
|{{from(direct).multicast().to(flaky)}}|{{context.start()}} throws
{{IllegalStateException: Cannot connect}}|
Stack of the split case:
{noformat}
IllegalStateException: Cannot connect
at ...FlakyComponent$1$1.doStart
at
org.apache.camel.support.service.ServiceHelper.startService(ServiceHelper.java:129)
at
org.apache.camel.support.AsyncProcessorConverterHelper$ProcessorToAsyncProcessorBridge.start(AsyncProcessorConverterHelper.java:116)
at
org.apache.camel.impl.engine.DeferServiceStartupListener.doStart(DeferServiceStartupListener.java:63)
at
org.apache.camel.impl.engine.DeferServiceStartupListener.onCamelContextStarting(DeferServiceStartupListener.java:49)
at
org.apache.camel.impl.engine.AbstractCamelContext.doStartCamel(AbstractCamelContext.java:3190)
...
at
org.apache.camel.impl.DefaultCamelContext.start(DefaultCamelContext.java:214)
{noformat}
We first hit this with a camel-paho-mqtt5 producer inside a split while the
broker host did not resolve; the same {{to}} outside the split was retried by
the controller. Any producer that connects in {{doStart}} behaves the same way.
*Cause* (main 04333431a65):
# {{MulticastProcessor.doInit}} (MulticastProcessor.java:313-321) calls
{{wrapInErrorHandler}} for every child processor. Split and multicast both use
it.
# {{wrapInErrorHandler}} starts the wrapped child right away: "must start the
error handler", {{ServiceHelper.startService(answer)}}
(MulticastProcessor.java:1213-1214). This starts the child's {{SendProcessor}}.
# {{SendProcessor.doStart}} creates the producer and adds it with
{{camelContext.addService(producer, true, true)}} (SendProcessor.java:335-350).
The context is not started yet, so {{InternalServiceManager}} defers it as an
early service (InternalServiceManager.java:164).
# {{DeferServiceStartupListener.onCamelContextStarting}} starts the early
services before any route starts, outside
{{DefaultSupervisingRouteController}}, and the exception fails the context.
For a {{to}} directly in the route, the producer is started by the route start,
which the supervising controller runs and retries.
*History*: The start inside {{wrapInErrorHandler}} dates from CAMEL-2360 /
CAMEL-3497, when the error handlers were created while processing the first
exchange, so the context and route were already started. Commit 7d70ad80c7d
("Support for the splitter eip in lightweight mode", 2020) moved the creation
into {{doInit}} for the lightweight CamelContext. CAMEL-19292 removed the
lightweight context in 4.0, and the eager creation in {{doInit}} stayed.
*Related*:
* CAMEL-18319: the supervising controller should not warm up routes during the
normal startup, so that start errors stay under its control. This is a
remaining eager start.
* CAMEL-21319: the test added there
(SmppTRXProducerSupervisingRouteControllerIT) shows a producer directly in the
route being restarted by the controller. {{lazyStartProducer}} was suggested
there as an alternative; it also avoids this failure, but the route then starts
and every message fails until the producer works, instead of the route being
supervised the same way as in the other EIPs.
* CAMEL-21775 (split/multicast in onException with the supervising controller)
is a different problem.
*Possible direction* (not tried): in {{doInit}} only create and initialize the
error handlers, and start them in {{doStart}}, which already starts
{{processors}} (MulticastProcessor.java:1280) and runs under the route start. I
can open a PR with the test, and with the fix if this direction is acceptable.
_Claude Code on behalf of etzel-mes_
--
This message was sent by Atlassian Jira
(v8.20.10#820010)