Federico Mariani created CAMEL-24091:
----------------------------------------

             Summary: camel-file: completion-time idempotentKey evaluated 
against route-mutated headers - failed files never retried (eager) or endless 
duplicates (non-eager); regression of CAMEL-21733
                 Key: CAMEL-24091
                 URL: https://issues.apache.org/jira/browse/CAMEL-24091
             Project: Camel
          Issue Type: Bug
          Components: camel-ftp, camel-file
    Affects Versions: 4.21.0
            Reporter: Federico Mariani
         Attachments: 
FileConsumerIdempotentRollbackChangedFileNameHeaderIssueTest.java

h3. Problem
When {{idempotentKey}} is an expression (the documented 
{{${file:name}-${file:size}}} style), the poll-time key is evaluated on a 
pristine file exchange ({{dynamic == null}} for scheduled polls). At 
completion, {{GenericFileOnCompletion.processStrategyCommit/Rollback}} 
(GenericFileOnCompletion.java:119, 162) re-evaluates the key via 
{{GenericFileHelper.createDummy(endpoint, exchange, ...)}} - and 
{{createDummy}} copies *all headers of the routed exchange over the dummy with 
override=true* ({{MessageHelper.copyHeaders(..., true)}}). Any route that 
mutates {{CamelFileName}}/{{CamelFileLength}} - the classic 
{{setHeader(Exchange.FILE_NAME, ...)}} before a file/ftp producer - makes the 
completion-time key differ from the poll-time key.

h3. Failure scenarios
* {{idempotentEager=true}} (default): rollback calls {{remove(wrongKey)}} -> 
the eagerly-added real key stays -> a *failed file is never retried* (silent 
loss).
* {{idempotentEager=false}}: commit calls {{add(wrongKey)}} -> real key never 
added -> with {{noop=true}} the file is *re-consumed on every poll* (endless 
duplicates).
* Eager + repos where {{confirm()}} is meaningful (JDBC/Kafka-style 
pending-confirm repos): {{confirm(wrongKey)}} leaves the real entry unconfirmed 
-> re-consumed after restart.

h3. Related variant (pre-existing)
With {{recursive=true}} + {{preMove}}, the OnCompletion holds the 
*post-preMove* file ({{GenericFileConsumer.java:470}}), and 
{{GenericFile.changeFileName}} keeps only the last path segment - so 
{{${file:name}}}-based keys differ from poll time even without header mutation, 
with the same failure modes.

h3. History
Before CAMEL-21733 (f27c9b212768, Camel 4.11) the completion dummy was 
{{endpoint.createExchange(file)}} - pure file headers. The enrichment was added 
so *dynamic poll* caller headers stay resolvable, but on the completion path 
the "dynamic" argument is the fully-routed exchange, overriding the very 
{{CamelFile*}} headers the key is computed from.

h3. Suggested fix
Snapshot the poll-time key on the exchange (property) and reuse it at 
commit/rollback - this fixes both the header-mutation regression and the 
preMove/recursive variant in one place. Alternatively, exclude {{CamelFile*}} 
headers from the completion-path enrichment.

h3. Reproducer
Attached failing JUnit test (place in 
{{core/camel-core/src/test/java/org/apache/camel/component/file/}}, where the 
file component tests live). Route sets {{CamelFileName}} then fails on first 
attempt; with the bug the rollback removes the wrong key and the file is never 
retried.

----
_This issue was researched and filed by Claude Code on behalf of [~croway] 
(GitHub: Croway), as part of a deep code review of camel-file and camel-ftp. A 
failing JUnit reproducer is attached; it fails deterministically on current 
main (4.22.0-SNAPSHOT)._



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

Reply via email to