You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@camel.apache.org by "shashank (Jira)" <ji...@apache.org> on 2026/09/28 11:41:00 UTC

[jira] [Commented] (CAMEL-25094) camel-seda, camel-disruptor - a message sent InOnly to seda: or disruptor: from inside a Multicast or Split loses its spooled stream cache file when the parent exchange completes

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

shashank commented on CAMEL-25094:
----------------------------------

PR: https://github.com/apache/camel/pull/26995

I cannot assign issues to myself; could a committer assign this to me (smjainblr)? Thanks.

_Claude Code on behalf of allthingssecurity_

> camel-seda, camel-disruptor - a message sent InOnly to seda: or disruptor: from inside a Multicast or Split loses its spooled stream cache file when the parent exchange completes
> ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
>
>                 Key: CAMEL-25094
>                 URL: https://issues.apache.org/jira/browse/CAMEL-25094
>             Project: Camel
>          Issue Type: Bug
>          Components: camel-disruptor, camel-seda
>            Reporter: shashank
>            Priority: Minor
>
> With stream caching spooled to disk ({{spoolEnabled=true}} and a body over the spool threshold), the body is a {{FileInputStreamCache}}. Its temporary file is deleted when the last exchange that holds it is done: {{FileInputStreamCache.TempFileManager}} counts one for every {{addExchange}} (the creation, and every {{StreamCache.copy(exchange)}}) and registers the matching countdown as an on completion ({{FileInputStreamCache.java:255}}). If the exchange has the property {{CamelStreamCacheUnitOfWork}}, the countdown is registered on that unit of work instead of on the exchange.
> Multicast and Split set {{CamelStreamCacheUnitOfWork}} to the parent's unit of work on every sub-exchange ({{MulticastProcessor.java:1127-1129}}, {{Splitter.java:415-416}}), so that the stream caches of the sub-routes live until the parent has aggregated them.
> A sub-exchange that sends to {{seda:}} without waiting for a reply (InOnly) hands a copy to the queue. {{SedaProducer.addToQueue}} creates the copy and, since CAMEL-20866 (4.7), gives it its own reference to the stream cache with {{sc.copy(target)}} ({{SedaProducer.java:227}}). The copy keeps {{CamelStreamCacheUnitOfWork}}, so that reference is registered on the parent's unit of work as well. The InOnly send returns at once, the parent finishes, its unit of work counts down both references and the file is deleted. The seda consumer then routes the copy and fails:
> {noformat}
> WARN SedaConsumer - Error processing exchange. Exchange[]. Caused by:
>   [org.apache.camel.StreamCacheException - Error during type conversion from type: null to the required type:
>    org.apache.camel.StreamCache due to org.apache.camel.RuntimeCamelException: Cannot reset stream from file ...]
> {noformat}
> The seda route never gets past its start, so the message is lost after the parent was told that it was sent. {{SedaConsumer}} logs the failure at WARN, as above. {{DisruptorProducer.prepareCopy}} ({{DisruptorProducer.java:234}}) does the same for {{disruptor:}}, and there the failure is not logged at all: the disruptor consumer processes the exchange with a no-op callback, so the message disappears without a log line.
> The Wire Tap EIP had exactly this problem and removes the property from its copy ({{WireTapProcessor.java:282}}, CAMEL-12108). In CAMEL-20866, SEDA (InOnly), Disruptor (InOnly) and Wire Tap were named as the cases where the copied exchange is executed independently, but only the Wire Tap drops the property.
> h3. Reproduction
> Stream caching with {{spoolEnabled=true}} and a small {{spoolThreshold}}, the body is a stream of 16 KB. The seda (or disruptor) route waits until the caller's {{sendBody}} has returned, so the parent exchange is done, and then reads the body.
> || route || main ||
> | {{multicast().to("seda:b", "mock:other")}} | fails every time, as above |
> | {{multicast().parallelProcessing().to("seda:b", "mock:other")}} | fails every time |
> | {{split(body()).to("direct:part")}} with {{from("direct:part").to("seda:b")}} (the parts are streams) | fails every time |
> | the same three with {{disruptor:b}} | fail every time, nothing is logged |
> | {{to("seda:b")}} or {{to("disruptor:b")}} without a Multicast or Split (control) | works |
> | {{recipientList(constant("seda:b,mock:other"))}} (control) | works: the Recipient List creates its own sub-exchanges and does not set the property |
> The wait in the seda route is not needed to trigger the failure: without it the multicast case still failed in 20 of 20 runs, because the parent always finishes first. The multicast case fails in the same way with the 4.6.0 {{SedaProducer}} (before the deep copy of CAMEL-20866): there the copy shares the parent's cache, whose file the parent's unit of work deletes. So this is not a regression of CAMEL-20866.
> A TLA+ model of the counter (parent, sub-exchange, seda copy, seda consumer) finds the same trace (send to seda, parent done, consumer reads a deleted file). The model without a Multicast and the model of the fix below hold, including that the file is always deleted in the end.
> h3. Proposed fix
> In {{SedaProducer.addToQueue}} and {{DisruptorProducer.prepareCopy}}, in the branch that copies the exchange, remove {{ExchangePropertyKey.STREAM_CACHE_UNIT_OF_WORK}} from the copy before {{sc.copy(target)}}, as the Wire Tap does. The copy then releases the file when it is done itself. The path that waits for a reply (InOut, {{waitForTaskToComplete}}) is not changed, because the producer waits for the copy there. With this change all the failing cases above read the full body, and no spool file is left behind afterwards. camel-stub extends {{SedaProducer}} and gets the same fix.
> Affected: long-standing, all 4.x versions (checked on main and with the 4.6.0 {{SedaProducer}}).
> Duplicate check (2026-09-28): JIRA text "Cannot reset stream from file" (CAMEL-21162, CAMEL-12108, CAMEL-12067, CAMEL-8688), "CamelStreamCacheUnitOfWork" (CAMEL-12108, CAMEL-13168 for direct-vm), "spool" with seda or disruptor: none about seda or disruptor. GitHub pull requests on "StreamCache", "spool", "FileInputStreamCache", "STREAM_CACHE_UNIT_OF_WORK": #14502 (CAMEL-20866) and CAMEL-25004 (stream caching strategy lifecycle), not this.
> _Filed with Claude Code on behalf of allthingssecurity._



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