[
https://issues.apache.org/jira/browse/CAMEL-26576?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Claus Ibsen updated CAMEL-26576:
--------------------------------
Fix Version/s: 4.24.0
> camel-core - Split/Multicast with shareUnitOfWork: a part sent to seda takes
> the consumer's commit, which then runs with the outcome of the seda copy
> -----------------------------------------------------------------------------------------------------------------------------------------------------
>
> Key: CAMEL-26576
> URL: https://issues.apache.org/jira/browse/CAMEL-26576
> Project: Camel
> Issue Type: Bug
> Components: camel-core
> Reporter: shashank
> Assignee: shashank
> Priority: Major
> Fix For: 4.24.0
>
>
> Found while modelling the shared unit of work of a parallel Split (see the
> linked handover race), then reproduced with deterministic tests on the real
> classes. It does not need parallel processing.
> Since Camel 4.14 (CAMEL-22259) the parts of a Split, Multicast or Recipient
> List with {{shareUnitOfWork}} use the parent's unit of work
> ({{MulticastProcessor.prepareSharedUnitOfWork}}). The 4.14 upgrade guide says
> the on completions of the parts now run "when the parent UnitOfWork is
> complete", and the Split docs describe {{shareUnitOfWork}} as a way to treat
> the whole message as one unit that either succeeds or fails.
> But a part that sends to an endpoint which routes a copy on its own hands
> over the on completions of its unit of work to that copy, for example
> {{seda}} with an {{InOnly}} exchange ({{SedaProducer.addToQueue}} ->
> {{ExchangeHelper.createCorrelatedCopy(exchange, true, true)}} ->
> {{AbstractExchange.handoverCompletions}} ->
> {{DefaultUnitOfWork.handoverSynchronization}}). With the shared unit of work
> that is all the on completions of the parent exchange, including the commit
> or rollback of its consumer ({{GenericFileOnCompletion}}, the SQS delete,
> ...). They then run when that seda copy is done, with the outcome of the
> copy, not of the split:
> * a later part fails the split, but the seda copy succeeds: the consumer
> commits. The file is moved to the done folder (not to {{moveFailed}}), an SQS
> message is deleted, so the failed message is not retried;
> * the split succeeds, but the seda copy fails: the consumer rolls back (the
> file goes to {{moveFailed}}, the SQS message is redelivered and all the parts
> run again);
> * the commit can run before the split has even finished.
> Before 4.14 each part had its own unit of work, so a part handed over only
> its own on completions. A part routed into a {{Resequencer}} or
> {{StreamResequencer}} hands over the same way. (The resource exchange of
> {{enrich}} with {{shareUnitOfWork}} has always shared the unit of work of the
> exchange, and can hand over its on completions to a seda copy too, for
> example with {{waitForTaskToComplete=Never}}; that is older and not part of
> this.)
> {code:java}
> from("file:inbox?move=done&moveFailed=failed")
> .split(body().tokenize(",")).shareUnitOfWork()
> .choice()
> .when(body().startsWith("a")).to("seda:lines")
> .when(body().isEqualTo("bad")).throwException(new
> IllegalArgumentException("bad line"))
> .end()
> .end();
> from("seda:lines").to("log:line");
> {code}
> A file with {{a,bad}} fails the split, but ends up in {{done}}, not in
> {{failed}}.
> On main (1b12eea, 2026-11-02), tests with that route, a Split, a Multicast, a
> Recipient List and a nested Split fail: the consumer on completion runs once,
> as {{onComplete}} for the seda copy instead of {{onFailure}} for the failed
> split exchange; and when only the seda copy fails, {{onFailure}} runs instead
> of {{onComplete}}. The same routes without the seda part, a plain
> {{to("seda:...")}} without a split (handover as designed, see CAMEL-9291),
> and a split where all parts succeed behave as expected.
> Related: CAMEL-22259 (the shared unit of work, 4.14), CAMEL-9291 (handover to
> seda from a plain route is by design), CAMEL-25094 (the stream cache of a
> seda copy sent from a Split/Multicast). Affects 4.14.0 and later.
> *Proposal*
> * A part that shares the unit of work of the parent exchange
> ({{prepareSharedUnitOfWork}}) hands over only the on completions it added to
> its own exchange, as before 4.14. The on completions of the parent (such as
> its consumer's commit) stay with the parent and run when the split is done,
> with its outcome. On completions added to the unit of work directly (such as
> by a route {{onCompletion}}) stay with the parent too.
> * A new {{ExchangeExtension.setSharedUnitOfWork(UnitOfWork)}} (default:
> {{setUnitOfWork}}) marks the exchange; it remembers the on completions it
> adds while it uses that unit of work and hands over only those
> ({{UnitOfWork.handoverSynchronization(target, filter)}}). A plain InOnly send
> to seda outside a split is unchanged.
> * Upgrade guide entry: the consumer now commits when the split is done,
> possibly before the seda copies are processed, so a copy that still reads the
> input (such as a file body not loaded yet) must load it first or wait for the
> copy ({{waitForTaskToComplete=Always}}).
> * Target main. A backport to 4.18.x (and 4.14.x) is possible as is (the
> method is a default method on the internal {{ExchangeExtension}}), but it
> changes when the consumer commits, so that is the committers' call.
> _Filed with Claude Code on behalf of allthingssecurity._
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)