[
https://issues.apache.org/jira/browse/CAMEL-25864?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18131724#comment-18131724
]
Claus Ibsen commented on CAMEL-25864:
-------------------------------------
Fixed by https://github.com/apache/camel/pull/28383, merged to main for 4.24.0.
_Claude Code on behalf of Claus Ibsen (davsclaus)_
> camel-core - Aggregate EIP: with an auto-discovered KeyValueRepository, the
> recover task of one aggregator sends the exchanges that another aggregator
> completed again, on its own route
> ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
>
> Key: CAMEL-25864
> URL: https://issues.apache.org/jira/browse/CAMEL-25864
> Project: Camel
> Issue Type: Bug
> Components: camel-core
> Reporter: shashank
> Assignee: shashank
> Priority: Major
> Fix For: 4.24.0
>
>
> An Aggregate EIP without an {{aggregationRepository}} wraps the single
> {{KeyValueRepository}} of the registry in a {{KeyValueAggregationRepository}}
> (CAMEL-24463; the "Single backend, multiple patterns" section of the
> KeyValueRepository page says no explicit wiring is needed). Every aggregator
> of the application does this with the same bean. Since CAMEL-25819 (main,
> 4.24.0) the wrapper is set with {{setAggregationRepository}}, so each of
> these aggregators now schedules a recover task.
> {{KeyValueAggregationRepository}} keeps a completed exchange, until it is
> confirmed, under {{completed:<exchangeId>}}, and {{scan}} returns every key
> with that prefix. So the recover task of aggregator B scans the completed
> exchanges of aggregator A as well. {{AggregateProcessor.RecoverTask}} only
> skips the exchange ids in its own {{inProgressCompleteExchanges}} /
> {{unconfirmedCompleteExchanges}}, so B recovers the exchange that A completed
> and is still processing, marks it redelivered and sends it on B's route
> ({{onSubmitCompletion}}), then confirms it. The exchange is delivered twice,
> once on a route that has nothing to do with it. The same happens to an
> exchange that failed on A's route: it is recovered by A and by B. The recover
> tasks run 1 s after start and then every 5 s (the default
> {{recoveryInterval}}, which cannot be changed for an auto-discovered
> repository), so under load every scan picks up the exchanges the other
> aggregators have in flight.
> {code:java}
> // one KeyValueRepository bean in the registry, e.g. a
> MemoryKeyValueRepository or a RedisKeyValueRepository
> from("direct:orders").routeId("orders")
> .aggregate(header("id"), new
> BodyInAggregatingStrategy()).completionSize(2)
> .process(e -> slowCall()) // the aggregated exchange is
> completed, not yet confirmed
> .to("mock:orders");
> from("direct:invoices").routeId("invoices")
> .aggregate(header("id"), new
> BodyInAggregatingStrategy()).completionSize(2)
> .to("mock:invoices"); // receives the "A+B" of the orders
> route
> {code}
> *Fix*: {{KeyValueAggregationRepository}} gets a constructor with a recovery
> name: the completed exchanges are then kept, scanned, recovered and confirmed
> under {{completed[<name>]:<exchangeId>}}, which an adapter without a name
> (prefix {{completed:}}) does not scan. {{AggregateProcessor}} names the
> adapter it creates for an auto-discovered repository after its route id and
> id ({{<routeId>/<id>}}). The open groups keep their keys
> ({{aggregate:<correlationKey>}}), and so does an adapter created without a
> name. No new bean property, so no generated metadata changes. Completed
> entries written under {{completed:}} by an auto-discovered adapter before
> (4.23.x, where they were never recovered, see CAMEL-25819) are not picked up
> by the named adapters.
> *Tests*: new {{AggregateAutoDiscoveredKeyValueRepositorySharedTest}}
> (camel-core): two routes with an aggregator and one
> {{MemoryKeyValueRepository}} bean; the orders route completes a group and
> holds the aggregated exchange downstream across the recover scans. On main it
> fails with {{mock://invoices Received message count. Expected: <0> but was:
> <1>}}; with the fix it passes, the orders route gets "A+B" once and the
> repository is empty at the end.
> {{KeyValueAggregationRepositoryTest.testRecoveryNameKeepsCompletedExchangesApart}}
> covers the adapter. camel-core aggregator and support tests
> ({{org.apache.camel.processor.aggregator.**}},
> {{org.apache.camel.support.**}}, ...): 707 run, 0 failures, 3 skipped;
> camel-support: 136 run, 0 failures.
> {{AggregateAutoDiscoveredKeyValueRepositoryRecoverTest}} (CAMEL-25819) still
> passes.
> *How it was found*: review of the changes merged to main in the last week
> (CAMEL-25819), looking at what the change made reachable: the recover task
> now runs for a repository that every aggregator of the application shares.
> The JUnit test above is the evidence.
> *Affected*: main only (read, not run). On 4.23.x the auto-discovered adapter
> has no recover task (CAMEL-25819 is fixed in 4.24.0 only), so the duplicates
> do not happen there. An application that explicitly wraps the same
> {{KeyValueRepository}} in a {{KeyValueAggregationRepository}} for several
> aggregators has the same overlap on every version with the adapter; the new
> constructor lets it name each adapter.
> _Filed with Claude Code on behalf of allthingssecurity._
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)