CAMEL-24138: Splitter - Fix failure tracker leaking into nested splits#24823
Conversation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Claus Ibsen <claus.ibsen@gmail.com>
|
🌟 Thank you for your contribution to the Apache Camel project! 🌟 🐫 Apache Camel Committers, please review the following items:
|
gnodet
left a comment
There was a problem hiding this comment.
Review: CAMEL-24138 — Splitter: Fix failure tracker leaking into nested splits
What works well
Clear root cause. SplitFailureTracker is stored as a plain exchange property (CamelSplitFailureTracker). Since exchange.copy() shallow-copies properties, a nested splitter inherits the outer's tracker. When the inner splitter has no thresholds, shouldContinueOnFailure() still finds the leaked tracker, records failures against it, clears the exception, and returns true — silently swallowing inner split failures and corrupting the outer tracker's totalItems count.
Minimal, targeted guards. Two changes, both checking errorThreshold > 0 || maxFailedRecords > 0:
shouldContinueOnFailure()— falls through tosuperwhen this splitter has no thresholds, preventing a threshold-less inner splitter from using the outer's trackerincrementTotalItems()— only accesses the tracker when this splitter has thresholds, preventing count corruption
Good test coverage. Three scenarios that exercise the key interactions:
- Inner failure propagates when outer has threshold but inner doesn't (the bug)
- Inner success still works with outer threshold (no regression)
- Both with thresholds — inner absorbs within its threshold, outer unaffected (both features compose)
Minor observation
A more thorough isolation (e.g., per-splitter-instance property keys) would fully prevent any tracker cross-talk, but this guard is sufficient, less invasive, and follows the principle of least surprise — a splitter without thresholds simply doesn't interact with the tracker at all. If tracker isolation becomes needed for other reasons, it can be revisited.
Checklist
| Item | Status |
|---|---|
| Tests | ✅ New 3-scenario test + 29 existing splitter threshold tests |
| Docs / Upgrade guide | N/A (bug fix for feature introduced in CAMEL-23264, same release cycle) |
| Commit convention | ✅ CAMEL-24138: <description> |
| Public API / backward compat | ✅ No API changes |
| Security | N/A |
| CI | ⏳ Pending |
Static analysis: semgrep (0 findings on Splitter.java from prior scan).
Clean and correct. No concerns.
Reviewed with Claude Code on behalf of gnodet. This review was generated by an AI agent and may contain inaccuracies; please verify all suggestions before applying.
oscerd
left a comment
There was a problem hiding this comment.
LGTM. The failure-tracker scoping is correct: errorThreshold/maxFailedRecords are per-instance and each split() builds a distinct Splitter, so the tracker only exists when that instance has thresholds. The added guard in shouldContinueOnFailure and the incrementTotalItems block means a leaked outer tracker (shallow-copied via exchange.copy()) is ignored by an inner threshold-less splitter, which falls through to super.shouldContinueOnFailure; the both-thresholds case is a no-op for the guard since the inner process() overwrites the property with its own tracker. Three scenarios covered.
Reviewed with Claude Code on behalf of Andrea Cosentino. This review was generated by an AI agent and may contain inaccuracies; please verify all suggestions before applying.
|
🧪 CI tested the following changed modules:
🔬 Scalpel shadow comparison — Scalpel: 544 tested, 29 compile-only — current: 544 all testedMaveniverse Scalpel detected 573 affected modules (current approach: 544).
|
Summary
Claude Code on behalf of davsclaus
The Splitter's
errorThreshold/maxFailedRecordsfeature (CAMEL-23264) stores itsSplitFailureTrackeras a plain exchange property (CamelSplitFailureTracker). Sinceexchange.copy()shallow-copies the properties map, this tracker leaks into any nested splitter running inside the outer split.When the inner splitter has no thresholds configured,
shouldContinueOnFailurestill finds the leaked outer tracker, records failures against it with inner indexes, clears the exception, and returnstrue— silently swallowing inner split failures.Fix: Guard
shouldContinueOnFailureand thetotalItemsincrement so they only activate when this splitter instance has thresholds configured (errorThreshold > 0 || maxFailedRecords > 0). A splitter without thresholds now falls through tosuper.shouldContinueOnFailure(), preserving normal failure propagation.maxFailedRecords/errorThresholdtotalItemscountTest plan
SplitterNestedFailureTrackerLeakTestwith 3 scenarios:maxFailedRecordsbut inner does notmaxFailedRecordsmaxFailedRecords— inner absorbs within threshold, outer unaffected🤖 Generated with Claude Code
Co-Authored-By: Claude Opus 4.6 noreply@anthropic.com