[ 
https://issues.apache.org/jira/browse/CAMEL-24954?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18118641#comment-18118641
 ] 

shashank commented on CAMEL-24954:
----------------------------------

Update: the PR (https://github.com/apache/camel/pull/26815) also includes the 
Cache EIP change. {{CacheProcessor.doStop()}} no longer stops its 
{{KeyValueRepository}}, and it is still stopped in {{doShutdown}}. The 
regression test now has 8 tests, covering the Cache EIP both when its route is 
stopped and when it is removed. The Cache EIP is new in 4.23, so it needs no 
upgrade-guide entry.

Note on history: for the Idempotent Consumer, this restores the {{doStop()}} 
from before CAMEL-9569. CAMEL-9569 added the repository stop there together 
with the actual leak fix in {{doShutdown()}}, and the leak fix is kept.

_Claude Code on behalf of allthingssecurity_

> Stopping a route with an Idempotent Consumer stops and clears a repository 
> that other running routes still use
> --------------------------------------------------------------------------------------------------------------
>
>                 Key: CAMEL-24954
>                 URL: https://issues.apache.org/jira/browse/CAMEL-24954
>             Project: Camel
>          Issue Type: Bug
>          Components: camel-core
>            Reporter: shashank
>            Priority: Minor
>
> {{IdempotentConsumer.doStop()}} stops the idempotent repository 
> ({{ServiceHelper.stopService(processor, idempotentRepository)}}). The 
> in-memory repositories clear their data when stopped:
> * {{MemoryIdempotentRepository.doStop()}} runs {{cache.clear()}}.
> * {{KeyValueIdempotentRepository.doStop()}} stops the underlying 
> {{KeyValueRepository}}, and {{MemoryKeyValueRepository.doStop()}} runs 
> {{store.clear()}}. That clears every entry in the store, not only the ones 
> with the {{idempotent:}} prefix.
> A repository is often shared: the same bean is referenced from several 
> routes. Since 4.23 it is also shared by default. When no repository is 
> configured, {{IdempotentConsumerReifier}} wraps the single 
> {{KeyValueRepository}} bean from the registry, and {{AggregateProcessor}} and 
> the Cache EIP do the same. So stopping one route silently turns off duplicate 
> detection for all the other routes, which are still running. With a shared 
> {{KeyValueRepository}}, it also throws away the aggregator's in-progress 
> groups.
> Reproducer:
> {code:java}
> from("direct:a").routeId("a").idempotentConsumer(header("id")).idempotentRepository(repo).process(e
>  -> count.incrementAndGet());
> from("direct:b").routeId("b").idempotentConsumer(header("id")).idempotentRepository(repo).log("b");
> {code}
> Send id=1 to {{direct:a}} twice (count=1), then call 
> {{context.getRouteController().stopRoute("b")}}, then send id=1 to 
> {{direct:a}} again. Route a is still started, but {{repo.contains("1")}} is 
> now false and the message is processed again (count=2). Results:
> * {{MemoryIdempotentRepository}}: count=2.
> * {{new KeyValueIdempotentRepository()}}: count=2.
> * No repository configured and a {{MemoryKeyValueRepository}} bean in the 
> registry (auto-discovered by both routes): count=2.
> * {{FileIdempotentRepository}}: count=1, because {{add}} falls back to the 
> file store.
> With a {{KeyValueIdempotentRepository}} and a 
> {{KeyValueAggregationRepository}} over the same {{MemoryKeyValueRepository}}, 
> the store contains {{[aggregate:g1, idempotent:1]}} before {{stopRoute}} on 
> the idempotent route and {{[]}} after it. The next message for group g1 then 
> starts a new group, so the first message is lost from the aggregate.
> The Cache EIP (new in 4.23) has the same pattern: {{CacheProcessor.doStop()}} 
> stops its {{KeyValueRepository}}. With one {{MemoryKeyValueRepository}} bean 
> auto-discovered by an idempotent consumer and a Cache EIP, {{stopRoute}} on 
> the cache route clears the store ({{[idempotent:1, K]}} before, {{[]}} 
> after), and the idempotent route processes id 1 again.
> A single route that is stopped and started also forgets all ids it has seen, 
> because its in-memory repository was cleared by the stop.
> Other code already handles this:
> * {{GenericFileEndpoint.doStart}} adds its idempotent repository as a context 
> service ("idempotent repository may be used by others, so add it as a service 
> so its stopped when CamelContext stops"), and its {{doStop}} does not stop 
> the repository.
> * {{AggregateProcessor}} stops its aggregation repository only in 
> {{doShutdown}}.
> Proposed fix: {{IdempotentConsumer.doStop()}} and {{CacheProcessor.doStop()}} 
> no longer stop the repository. {{doStart}} and {{doShutdown}} are unchanged, 
> so the repository is still stopped and shut down when the route is removed or 
> the CamelContext stops (CAMEL-9569), like {{AggregateProcessor}} does. 
> Behaviour change for the 4.23 upgrade guide: a route that is stopped and 
> started again keeps the ids in its in-memory repository, and a repository 
> backed by a remote store stays started while the route is stopped 
> ({{IdempotentRepository.clear()}} or the {{clear}} JMX operation forgets the 
> ids). A PR with the fix follows, with regression tests.
> Not covered by the fix (possible follow-ups):
> * Removing a route (rather than stopping it) still shuts down the repository, 
> so removing one of several routes that share an in-memory repository still 
> clears it for the others. This is the existing CAMEL-9569 trade-off; fixing 
> it needs reference counting or an ownership flag.
> * {{KeyValueIdempotentRepository.doStop()}} (and 
> {{KeyValueAggregationRepository}}) stops the {{KeyValueRepository}} that was 
> passed in, although the adapter does not own it.
> Found with a TLA+ model of the Idempotent Consumer EIP, then reproduced 
> against 4.23.0-SNAPSHOT.



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

Reply via email to