shashank created CAMEL-25759:
--------------------------------

             Summary: camel-aws2-eventbridge - a consumer with queueUrl adds a 
new target for the same queue at every start, so two consumers sharing the 
queue, or a restart after a crash, put every event on the queue twice
                 Key: CAMEL-25759
                 URL: https://issues.apache.org/jira/browse/CAMEL-25759
             Project: Camel
          Issue Type: Bug
          Components: camel-aws2
            Reporter: shashank
            Assignee: shashank


The EventBridge consumer (since 4.19) wires an SQS queue as a target of the 
rule and polls the queue. {{EventbridgeConsumer.setupConsumerResources}} (main 
e13303ef1ef7, line 188) gives the target a new random id at every start:

{code:java}
targetId = "camel-eb-" + UUID.randomUUID().toString().substring(0, 8);
...
ebClient.putTargets(PutTargetsRequest.builder().rule(ruleName).eventBusName(...).targets(target).build());
{code}

and {{cleanupConsumerResources}} (lines 210-227) removes that target on stop. 
With an auto-created queue each consumer has its own queue, so this is fine. 
With a queue given by the user ({{queueUrl}}, the documented "User-Provided 
Queue" setup; it is also the only way to let several application instances 
share the events of a rule, since with auto-created queues every instance gets 
its own copy of every event) the rule gets one target per consumer for the 
*same* queue:

# two consumers of the queue run at the same time (two instances of the 
application, or two routes): the rule has two targets with the queue ARN. 
EventBridge sends a matching event to every target of the rule, so each event 
is put on the queue twice and routed twice;
# a consumer is not stopped cleanly (JVM killed, container OOM, node lost): its 
target stays on the rule. The next start adds a second target for the queue, 
and from then on every event is put on the queue twice, with no error and 
nothing in the log. Each such restart adds one more copy, until the rule 
reaches the EventBridge limit of 5 targets and the consumer fails to start.

In addition, the target is removed when the consumer stops, so a user-owned 
queue does not collect the events of the rule while the application is 
restarted or redeployed, and with two consumers the first one that stops 
removes its own target only, so the duplicates last as long as both run.

*Fix*: the target of a user-provided queue gets an id derived from the queue 
ARN ({{camel-eb-}} + 32 hex characters of its SHA-256). PutTargets replaces a 
target with the same id, so every start and every consumer of the queue updates 
the one target. That target is kept when the consumer stops (other consumers 
may share the queue, and the queue keeps collecting events while no consumer 
runs). An auto-created queue keeps its random target id and its clean-up 
(target removed, queue deleted with {{deleteQueueOnShutdown=true}}). The 
component page and the 4.24 upgrade guide say so; a target left by an earlier 
version (id {{camel-eb-}} + 8 characters) has to be removed by hand.

*Design choice to review*: keeping the target of a user-provided queue on stop 
reverses what CAMEL-23207 did (the component page said "On shutdown the target 
is removed", and {{testUserProvidedQueueNotDeletedOnShutdown}} verified the 
removal). The two changes go together: with a shared id but removal on stop, 
the first consumer that stops would remove the target that the others still 
use. Not changed: a target that the user added to the rule for the same queue 
(with an id of their own, for example from infrastructure as code) is still a 
second target, as on main.

*Tests*: new {{EventbridgeConsumerUserQueueTargetTest}} (4 tests): the mocked 
EventBridge client keeps the rule as a map of target id to ARN, with the 
PutTargets (replace by id) and RemoveTargets semantics. 
{{twoConsumersSharingAQueueAddOneTarget}} and 
{{restartAfterACrashAddsNoSecondTarget}} fail on main ({{expected: <1> but was: 
<2>}}, two {{camel-eb-xxxxxxxx}} targets for the user queue); 
{{userQueueKeepsCollectingEventsWhileTheConsumerIsStopped}} fails on main 
(target removed); {{autoCreatedQueueTargetIsStillRemovedOnStop}} passes on main 
and with the fix (the auto-created path is unchanged). 
{{EventbridgeConsumerTest.testUserProvidedQueueNotDeletedOnShutdown}} pinned 
the removal of the user queue's target and now checks that the target is kept. 
camel-aws2-eventbridge unit tests: 30 run, 0 failures (the LocalStack ITs need 
Docker and were not run).

*How it was found*: a TLA+ model of the targets of the rule (consumers sharing 
one user queue that start, stop or crash; each published event is put on the 
queue once per target). TLC finds a violation of "no event is put on the queue 
twice" in 4 steps with two consumers (start, start, publish) and with one 
consumer (start, crash, start, publish); with the fix the property, "an event 
published while a consumer runs reaches the queue" and "once a consumer 
started, every later event reaches the queue" hold for 2 and 3 consumers. The 
JUnit tests above are the evidence; the EventBridge semantics (every target is 
invoked; PutTargets with an existing id updates that target) are from the AWS 
API reference and were not run against AWS.

*Affected*: main, camel-4.23.x, camel-4.22.x and camel-4.19.x (the same lines; 
read, not run). camel-4.18.x has no consumer.

_Filed with Claude Code on behalf of allthingssecurity._




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

Reply via email to