shashank created CAMEL-24944:
--------------------------------

             Summary: Aggregate EIP: with optimistic locking and a 
pre-completing AggregationStrategy, a completed group can be lost
                 Key: CAMEL-24944
                 URL: https://issues.apache.org/jira/browse/CAMEL-24944
             Project: Camel
          Issue Type: Bug
          Components: camel-core
            Reporter: shashank


With {{optimisticLocking()}} and an {{AggregationStrategy}} that uses 
pre-completion ({{canPreComplete()}} returns true), a group that was 
pre-completed can be removed from the repository and never sent.

In pre-completion mode {{doAggregation}} first completes the existing group: it 
removes it from the repository and keeps it in a local list. Then it stores the 
new exchange as the first exchange of a new group with {{add(key, null, 
newExchange)}}. If another thread created a new group for the same key in 
between, that add throws {{OptimisticLockingException}}. The exception leaves 
{{doAggregation}} before the local list is returned, and {{doInOptimisticLock}} 
retries the exchange from the beginning. The group that was already removed is 
never sent.

Example, key 1, strategy pre-completes when the body starts with {{START}}:
# {{a1}} is aggregated. The repository holds group [a1].
# {{START-b}} arrives. Its thread removes [a1] from the repository 
(pre-completion).
# Before it stores its new group, {{c}} arrives on another thread and creates 
group [c].
# The add of {{START-b}} fails with {{OptimisticLockingException}} and is 
retried. The retry pre-completes [c] and starts [START-b].

Observed with {{MemoryAggregationRepository(true)}} and the {{START-b}} thread 
paused after its {{remove()}}; the remaining groups are flushed with 
{{forceCompletionOfAllGroups()}} at the end:
{noformat}
downstream output: [c (completedBy=strategy), START-b (completedBy=force)]
{noformat}
{{a1}} is never delivered. Without the pause the output is {{[a1 
(completedBy=strategy), START-b+c (completedBy=force)]}}.

Cause: {{AggregateProcessor.doProcess}} catches only {{CamelExchangeException}} 
around {{doAggregation}}, so the {{OptimisticLockingException}} thrown by 
{{doAggregationRepositoryAdd}} after a successful pre-completion discards the 
list of completed exchanges.

The same list is dropped in pessimistic mode too, whenever the rest of 
{{doAggregation}} does not finish normally after a pre-completion:
* the strategy throws when it aggregates the new exchange: the new exchange 
fails as expected, but the pre-completed group is lost as well,
* the same with {{discardOnAggregationFailure}}: {{doAggregation}} returns 
{{null}} for the discarded first exchange of the new group, and the list with 
the pre-completed group is dropped.

With a recoverable repository the lost group was eventually re-delivered by the 
recover task (from its completed store). With {{MemoryAggregationRepository}} 
it was lost.

Proposed fix: {{doAggregation}} adds completed exchanges to a list owned by 
{{doProcess}}, and {{doProcess}} submits that list in a {{finally}} block after 
releasing the lock. So groups that were already completed (and removed from the 
repository) are sent even if the aggregation fails afterwards or the exchange 
is retried because of optimistic locking.



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

Reply via email to