[ 
https://issues.apache.org/jira/browse/CAMEL-26321?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Work on CAMEL-26321 started by shashank.
----------------------------------------
> camel-core - Split EIP: when maxFailedRecords or errorThreshold stops the 
> split, the exchange carries the message, headers and properties of the failed 
> split item instead of the original input (unlike stopOnException)
> -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
>
>                 Key: CAMEL-26321
>                 URL: https://issues.apache.org/jira/browse/CAMEL-26321
>             Project: Camel
>          Issue Type: Bug
>          Components: camel-core
>            Reporter: shashank
>            Assignee: shashank
>            Priority: Major
>
> When the Splitter stops because {{maxFailedRecords}} or {{errorThreshold}} is 
> reached (CAMEL-23264, since 4.22.0), the exchange gets the message, headers 
> and exchange properties of the split item that reached the threshold, as the 
> steps inside the split changed them, instead of the original input (main 
> 426c3f8a4cb8). With {{stopOnException}}, the all-or-nothing variant, the 
> exchange keeps the original input and only gets the exception (CAMEL-6802):
> {code:java}
> from("direct:max").split(body()).maxFailedRecords(1)
>     .setHeader("item", simple("${body}")).setBody(constant("changed"))
>     .throwException(new IllegalStateException("boom")).end();
> send body "a,b,c" with header orig=yes:
> maxFailedRecords(1)            -> body=changed headers={orig=yes, item=a} 
> exception=CamelExchangeException
> maxFailedRecords(2)            -> body=changed headers={orig=yes, item=b} 
> exception=CamelExchangeException
> stopOnException() instead      -> body=a,b,c   headers={orig=yes}         
> exception=CamelExchangeException
> {code}
> The exchange properties of the item are merged into the exchange: one set in 
> the item is there afterwards, one the item removed is still there. Its 
> exchange variables are copied too. {{errorThreshold}}, 
> {{parallelProcessing}}, {{streaming}}, {{shareUnitOfWork}} and a transacted 
> exchange behave the same. With nested splits the outer exchange gets the 
> failed outer item (or, when the inner split has a threshold too, the failed 
> inner item). The split EIP page says "The Splitter will by default return the 
> original input message", and that when the threshold is reached "the splitter 
> stops and sets an exception on the exchange". In the CAMEL-23264 PR (#22300) 
> zero tolerance is "{{stopOnException}} or {{maxFailedRecords(1)}}", but the 
> two return different messages.
> So a {{doTry}}/{{doCatch}} around the split, an {{onException}} in a calling 
> route or an outer dead letter channel that puts the failed input aside after 
> the stop keeps one modified split item instead of the input. The items after 
> it were not processed and are not in what was kept, so they are lost. (With a 
> dead letter channel or a handled {{onException}} on the route of the split 
> itself, the exceptions of the items are handled inside the split and the 
> threshold is never reached.)
> *Fix* (proposed): both stops go through the same branch of the multicast task 
> ({{shouldContinueOnFailure}} returns false), but 
> {{MulticastProcessor.doDone}} only takes the stopOnException path (set the 
> exception, do not copy the sub-exchange back) when the {{stopOnException}} 
> option is set; otherwise it runs {{ExchangeHelper.copyResults(original, 
> subExchange)}}. The Splitter now marks its failure tracker when a threshold 
> stops the split, and {{doDone}} also takes that path when a new protected 
> {{isStoppedOnFailure(original)}} says so (false in {{MulticastProcessor}}). 
> The exchange then gets the same exception and properties as with 
> {{stopOnException}}, and an aggregation strategy sees the stop the same way 
> ({{onCompletion}} with the failed sub-exchange, the items before it 
> aggregated, the aggregated result not returned). The {{CamelSplitResult}} 
> property is set as before. Split EIP docs and an upgrade guide entry, as a 
> route now receives the original input after such a stop.
> *Tests*: {{SplitterThresholdStopKeepsOriginalMessageTest}} (14 tests: 
> maxFailedRecords 1 and 2, errorThreshold, parallel, streaming, 
> shareUnitOfWork, transacted, two nested-split cases, a doTry/doCatch around 
> the split, an aggregation strategy and the exception and property set 
> compared with stopOnException, and two stopOnException controls). The 12 
> non-control tests fail on main and all pass with the fix. The Splitter, 
> Multicast, RecipientList and threshold tests of camel-core pass (613 tests).
> *How it was found*: a Rocq specification of Camel's EIPs written from the 
> documentation, compared with real Camel on generated routes (12 of 3 000 
> split cases differed, all with {{maxFailedRecords}}), then reproduced on 
> HotSpot.
> *Affected*: 4.22.0 and later (4.22.0, 4.22.1, 4.22.2, 4.23.0 and main; 
> camel-4.22.x has the same code). Not 4.18.x or 4.14.x, which do not have 
> these options.
> _Filed with Claude Code on behalf of allthingssecurity._



--
This message was sent by Atlassian Jira
(v10.3.26#10030026)

Reply via email to