shashank created CAMEL-25869:
--------------------------------

             Summary: camel-pulsar - with Shared, Failover or Key_Shared 
subscriptions, a consumer that cannot subscribe is dropped: the route starts 
with fewer consumers than configured, or none, and never subscribes them again
                 Key: CAMEL-25869
                 URL: https://issues.apache.org/jira/browse/CAMEL-25869
             Project: Camel
          Issue Type: Bug
          Components: camel-pulsar
            Reporter: shashank
            Assignee: shashank


With {{subscriptionType}} {{Shared}}, {{Failover}} or {{Key_Shared}}, the 
consumer creation strategies ({{SharedConsumerStrategy}}, 
{{FailoverConsumerStrategy}}, {{KeySharedConsumerStrategy}}, main 387cab56627b, 
lines 51-62 of each) subscribe {{numberOfConsumers}} consumers in a loop and 
catch the failure of each subscribe:

{code:java}
for (int i = 0; i < configuration.getNumberOfConsumers(); i++) {
    final String consumerName = configuration.getConsumerNamePrefix() + i;
    try {
        ...
        
consumers.add(builder.subscriptionType(SubscriptionType.Shared).subscribe());
    } catch (PulsarClientException exception) {
        LOGGER.error("A PulsarClientException occurred when creating Consumer 
{}, {}", consumerName,
                exception.getMessage(), exception);
    }
}
return consumers;
{code}

{{PulsarConsumer.doStart}} keeps whatever came back. So when a subscribe fails, 
the route still reports Started, with fewer consumers than configured, or with 
none, and nothing subscribes the missing ones again: there is no Pulsar 
consumer object left that could reconnect. In the Pulsar client (4.2.4, 
{{ConsumerImpl.connectionFailed}}), {{subscribe()}} throws once the broker 
could not be reached before the operation timeout (30 s by default), or on an 
error it does not retry; the consumer is then cleaned up from the client. 
Typical cases: the application starts while the broker restarts or is not 
reachable yet, a topic lookup that times out.

Consequences:
* {{numberOfConsumers=1}} (the default): the route is Started and holds no 
consumer. Nothing is consumed and the messages stay in the subscription until 
the route or the application is restarted. Only one ERROR line in the log.
* {{numberOfConsumers=N}}: the route consumes with fewer consumers than 
configured, without notice (less throughput; with {{Key_Shared}} and 
{{Failover}} the subscription is spread over fewer consumers than planned).
* The supervising route controller cannot help, as the start did not fail.

With {{Exclusive}} the failure is not caught, and the route fails to start 
({{ExclusiveConsumerStrategy}}), so the two paths behave differently for the 
same broker error.

*Fix*: the three strategies share one loop 
({{CommonCreationStrategyImpl.createMultipleConsumers}}). When a subscribe 
fails, the consumers subscribed so far are closed and the failure is thrown, so 
the route fails to start as with {{Exclusive}}; the caller or the supervising 
route controller can start it again, and a start that succeeds holds all the 
configured consumers. Upgrade note in the 4.24 guide, as a route that used to 
start with missing consumers now fails to start.

*Tests*: new {{PulsarConsumerSubscribeFailureTest}} (parameterised over 
{{SHARED}}, {{FAILOVER}}, {{KEY_SHARED}}; a mocked {{PulsarClient}} whose 
second subscribe throws {{PulsarClientException.TimeoutException}}, 
{{numberOfConsumers=2}}). On main all three fail with {{Expected 
java.lang.Exception to be thrown, but nothing was thrown}} (the route starts 
with one consumer). With the fix: the start fails, the consumer that did 
subscribe is closed, and a second start opens both consumers. camel-pulsar unit 
tests with the fix: 33 run, 0 failures.

*How it was found*: deterministic simulation of the camel-pulsar consumer 
against a fake Pulsar client (subscribe failures, broker restarts, route 
restarts, suspend/resume, slow and failing processing; an operator that starts 
the route again when its start fails, as the supervising route controller 
does). The invariant "a started route holds the configured number of Pulsar 
consumers" failed in 57 of 300 seeds, first reported on seed 9: {{FAILOVER 
numberOfConsumers=2}}, the subscribe of the first consumer timed out, the route 
reported Started with one consumer. In seed 8 ({{KEY_SHARED 
numberOfConsumers=1}}) the route was Started with no consumer, and none of the 
11 published messages was routed in more than 5 simulated minutes after the 
faults cleared. With the fix, 300 of 300 seeds pass; without subscribe 
failures, main passes 300 of 300 (no other invariant failed). The JUnit test 
above is the evidence; no run against a real broker.

*Affected*: main, camel-4.23.x, camel-4.22.x and camel-4.18.x (the same catch 
in the three strategies; 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