shashank created CAMEL-25123:
--------------------------------

             Summary: camel-seda - a message that is discarded 
(discardWhenFull) or not added to a full queue loses the on completions of its 
exchange, so a file consumer never commits or rolls back the file
                 Key: CAMEL-25123
                 URL: https://issues.apache.org/jira/browse/CAMEL-25123
             Project: Camel
          Issue Type: Bug
          Components: camel-seda, camel-disruptor
            Reporter: shashank


For an InOnly exchange, {{SedaProducer.addToQueue}} creates a copy with 
{{prepareCopy(exchange, true)}}, which hands the on completions of the exchange 
over to the copy: the consumer's commit and rollback (such as the file 
consumer's {{GenericFileOnCompletion}}), {{onCompletion}} synchronizations, and 
the release of a spooled stream cache. The copy is then added to the queue. 
When the copy is not added, it is dropped together with those on completions, 
and they never run:
* {{discardWhenFull=true}} and the queue is full: the copy is discarded, and 
the exchange completes as a success.
* the queue is full ({{Queue full}} {{IllegalStateException}}), the 
{{offerTimeout}} elapses ({{Fails to insert element into queue}}), or the 
producer is interrupted: the exchange fails, but its rollback does not run.

The Disruptor producer has the same problem when the ring buffer is full with 
{{blockWhenFull=false}} (the component default is {{true}}) or the Disruptor is 
not started: {{DisruptorProducer}} hands the on completions over to the copy 
before {{doPublish}} throws.

Reproduced with a real file consumer, 
{{from("file:in").to("seda:q?size=1&discardWhenFull=true")}} and a slow 
consumer on {{seda:q}}, 4 files:
{noformat}
discardWhenFull:  1 consumed, 3 files left in the input directory, in-progress 
repository size 3
offerTimeout=50:  1 consumed, 3 files left in the input directory, in-progress 
repository size 3
size=1 (full):    1 consumed, 3 files left in the input directory, in-progress 
repository size 3
size=10 (control): 4 consumed, 4 files moved to .camel, in-progress repository 
size 0
{noformat}
The files of the dropped copies are neither moved nor deleted, and as they stay 
in the file consumer's in-progress repository, they are not picked up again 
(not even after the queue has room again) until the route or the application is 
restarted. With {{offerTimeout}} or a full queue, the exchange failed, so the 
file should have been rolled back and retried.

With stream caching spooled to disk, the spool file of every discarded copy is 
also left behind until the context stops (a direct route with 4 spooled 
messages to a full queue with {{discardWhenFull}}: 1 of 4 on completions of the 
original exchanges ran, 3 spool files left).

h3. Proposed fix

When the copy is not added to the queue (discarded, or adding it failed), hand 
its on completions back to the exchange 
({{target.getExchangeExtension().handoverCompletions(exchange)}}), so they run 
when the exchange is done, with its outcome:
* a discarded message completes as before as a success, so its consumer now 
commits it (for example the file is moved to {{.camel}}), as for any message 
whose route completed;
* a message that could not be added fails as before, so its consumer now rolls 
it back, and can pick it up again.
The same in {{DisruptorProducer}} when {{doPublish}} fails. This also releases 
the spool file of the copy.

This differs from CAMEL-24950 (purging the queue), where the copy was accepted 
by the queue and the exchange that sent it had already completed, so the 
dropped copy runs the on completions as a failure itself.

With the fix: discardWhenFull commits the 3 discarded files; offerTimeout and a 
full queue roll back the files, which are retried and all 4 are consumed; no 
spool file is left behind, and all on completions run. This changes what 
happens to a discarded message, so it needs an upgrade guide note.

Affected: all 4.x versions (the hand-over to the copy is long-standing).

Duplicate check (2026-09-29): JIRA "discardWhenFull" (CAMEL-24948, the 
interrupted paths, fixed on main), "offerTimeout" with "seda" (CAMEL-12584, the 
option itself, and CAMEL-24948), "discard" with "onCompletion" and "seda" 
(CAMEL-24950, the purge path, fixed). GitHub pull requests "discardWhenFull": 
nothing on this.

_Filed with Claude Code on behalf of allthingssecurity._




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

Reply via email to