Skip to content

CAMEL-25082: camel-disruptor - mark the published copy, not the caller's exchange, as ignored on a request/reply timeout - #26980

Merged
davsclaus merged 1 commit into
mainfrom
fix/CAMEL-25082
Sep 28, 2026
Merged

davsclaus merged 1 commit into
mainfrom
fix/CAMEL-25082

Conversation

@davsclaus

Copy link
Copy Markdown
Contributor

Description

CAMEL-25082, found in the review of CAMEL-25038 (#26913).

On a request/reply timeout DisruptorProducer set DisruptorEndpoint.DISRUPTOR_IGNORE_EXCHANGE on the caller's exchange, not on the copy it had published into the ring buffer. DisruptorConsumer ignores any exchange that has the property (containsKey, whatever its value). So:

  • a timed out copy that the consumer had not started yet was still processed (the property never reached it);
  • every later copy of the caller's exchange inherited the property (prepareCopy copies the properties) and was dropped by the consumer, with only a TRACE log:
    • a redelivery by the error handler after the timeout was dropped and timed out again, so the redeliveries could never succeed;
    • a fallback to another disruptor endpoint, e.g. doCatch(ExchangeTimedOutException.class).to("disruptor:fallback"), was dropped: an InOnly message was lost silently, an InOut one timed out again.

Change

  • The producer puts its completed flag (the AtomicBoolean that the reply, the timeout and the interrupt already claim since CAMEL-25038) on the published copy before it publishes it. The consumer ignores the copy only when the producer no longer waits for it, which now also covers an interrupted wait. The property is set before publishing, so the property map is not changed while the consumer reads it; only the AtomicBoolean changes afterwards.
  • The flag stays out of the routed exchange (the consumer removes it from its copy), is not copied back into the caller's exchange with the reply (copyResults copies the properties), and is not inherited by a new copy of the caller's exchange.
  • DisruptorConsumer and the reconfiguration buffer in DisruptorReference check the value of the flag through DisruptorEndpoint.isIgnoreExchange.
  • Upgrade guide 4.23: a paragraph in the existing camel-disruptor request/reply entry.

Compared with camel-seda

camel-seda is not affected: on a timeout (and on an interrupt) SedaProducer removes the timed out copy from its queue (endpoint.getQueue().remove(copy)) and does not mark the caller's exchange.

Tests

New DisruptorTimeoutIgnoreExchangeTest:

  • a redelivery after a timeout is processed and gets the reply;
  • an InOnly fallback to another disruptor endpoint after a timeout is delivered;
  • a timed out exchange that the consumer had not started is not processed;
  • a successful reply does not leave the flag on the caller's exchange, and a following InOnly send is delivered.

Without the main-code change the first three fail (CamelExecutionException from the timed out redeliveries, mock://fallback ... Expected: <1> but was: <0>, mock://busy ... Expected: <1> but was: <2>). With it, the whole camel-disruptor suite passes (110 tests).

Claude Code on behalf of davsclaus

🤖 Generated with Claude Code

…r's exchange, as ignored on a request/reply timeout

Cause: on a request/reply timeout DisruptorProducer set the
disruptor.ignoreExchange property on the caller's exchange instead of the
copy it had published into the ring buffer. The consumer ignored any
exchange that had the property (containsKey, whatever the value).

Effect: a timed out copy that the consumer had not started yet was still
processed, and every later copy of the caller's exchange inherited the
property and was dropped by the consumer: a redelivery after the timeout
timed out again, and an InOnly fallback to another disruptor endpoint was
lost silently.

Fix: the producer puts its completed flag (the AtomicBoolean that the reply,
the timeout and the interrupt already claim) on the published copy before
publishing it, so the consumer ignores the copy only when the producer no
longer waits for it. The flag is not copied into the routed exchange, not
copied back into the caller's exchange with the reply, and not inherited
by a new copy. The consumer and the reconfiguration buffer check the value
of the flag. camel-seda is not affected, as SedaProducer removes the timed
out copy from its queue.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Signed-off-by: Claus Ibsen <claus.ibsen@gmail.com>
@github-actions

Copy link
Copy Markdown
Contributor

🌟 Thank you for your contribution to the Apache Camel project! 🌟
🤖 CI automation will test this PR automatically.

🐫 Apache Camel Committers, please review the following items:

  • First-time contributors require MANUAL approval for the GitHub Actions to run
  • You can use the command /component-test (camel-)component-name1 (camel-)component-name2.. to request a test from the test bot although they are normally detected and executed by CI.
  • You can label PRs using skip-tests and test-dependents to fine-tune the checks executed by this PR.
  • Build and test logs are available in the summary page. Only Apache Camel committers have access to the summary.

⚠️ Be careful when sharing logs. Review their contents before sharing them publicly.

@gnodet-bot gnodet-bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solid fix. The core bug was that the timeout path marked the caller's exchange instead of the published copy — so the consumer never saw the flag on the timed-out copy, and every subsequent copy of the caller (redelivery, fallback) inherited the mark and was silently dropped.

The fix correctly reuses the completed AtomicBoolean (already shared between timeout/reply/interrupt paths since CAMEL-25038) as the ignore flag on the published copy. The happens-before relationship through AtomicBoolean.get()/compareAndSet() is correct for the producer→consumer handoff. All leak paths are cleaned:

  • prepareCopy() strips it from new copies (no inheritance)
  • prepareExchange() strips it from the routed copy (no leak into the route)
  • onDone strips it after copyResults (no leak back to caller)

isIgnoreExchange() defensively handles both the new AtomicBoolean and the legacy Boolean.TRUE value — good.

Tests cover all four scenarios from the description (redelivery, InOnly fallback, timed-out-before-consumer, no-leak-on-success). Upgrade guide paragraph is clear.

No issues found.

This review was generated by an AI agent, Hermès on behalf of @gnodet.

@github-actions

Copy link
Copy Markdown
Contributor

🧪 CI tested the following changed modules:

  • components/camel-disruptor
  • docs

🔬 Scalpel shadow comparison — Scalpel: 9 of 695 tested, 26 compile-only — current: 9 all tested

Maveniverse Scalpel detected 9 affected modules (current approach: 9).

Skip-tests mode would test 9 modules (2 direct + 8 downstream), skip tests for 26 (generated code, meta-modules)

Modules Scalpel would test (9)
  • camel-disruptor ← components/camel-disruptor/src/main/java/org/apache/camel/component/disruptor/DisruptorConsumer.java, components/camel-disruptor/src/main/java/org/apache/camel/component/disruptor/DisruptorEndpoint.java, components/camel-disruptor/src/main/java/org/apache/camel/component/disruptor/DisruptorProducer.java, components/camel-disruptor/src/main/java/org/apache/camel/component/disruptor/DisruptorReference.java, components/camel-disruptor/src/test/java/org/apache/camel/component/disruptor/DisruptorTimeoutIgnoreExchangeTest.java
  • camel-jbang-mcp ← downstream of org.apache.camel:camel-catalog
  • camel-jbang-plugin-mcp ← downstream of org.apache.camel:camel-jbang-core
  • camel-jbang-plugin-route-parser ← downstream of org.apache.camel:camel-route-parser
  • camel-jbang-plugin-tui ← downstream of org.apache.camel:camel-catalog
  • camel-jbang-plugin-validate ← downstream of org.apache.camel:camel-yaml-dsl-validator
  • camel-launcher-container ← downstream of org.apache.camel:camel-launcher
  • camel-yaml-dsl-validator ← downstream of org.apache.camel:camel-catalog
  • camel-yaml-dsl-validator-maven-plugin ← downstream of org.apache.camel:camel-yaml-dsl-validator
Modules with tests skipped (26)
  • apache-camel
  • camel-allcomponents
  • camel-catalog
  • camel-catalog-console
  • camel-catalog-maven
  • camel-catalog-suggest
  • camel-componentdsl
  • camel-endpointdsl
  • camel-endpointdsl-support
  • camel-itest
  • camel-jbang-core
  • camel-jbang-it
  • camel-jbang-main
  • camel-jbang-plugin-edit
  • camel-jbang-plugin-generate
  • camel-jbang-plugin-kubernetes
  • camel-jbang-plugin-test
  • camel-kamelet-main
  • camel-launcher
  • camel-report-maven-plugin
  • camel-route-parser
  • camel-yaml-dsl
  • camel-yaml-dsl-deserializers
  • camel-yaml-dsl-maven-plugin
  • coverage
  • dummy-component

ℹ️ Shadow mode — Scalpel observes but does not affect test execution. Learn more

All tested modules (36 modules, 5m 12s total)

Total reactor time: 5m 12s

Module Duration Status
Camel :: Launcher 49.4s SUCCESS
Camel :: JBang :: MCP 39.9s SUCCESS
Camel :: JBang :: Plugin :: TUI 37.1s SUCCESS
Camel :: Component DSL 26.1s SUCCESS
Camel :: Catalog :: Camel Catalog 22.7s SUCCESS
Camel :: YAML DSL 18.7s SUCCESS
Camel :: JBang :: Plugin :: Kubernetes 14.9s SUCCESS
Camel :: Docs 14.9s SUCCESS
Camel :: Kamelet Main 12.0s SUCCESS
Camel :: YAML DSL :: Validator 10.4s SUCCESS
Camel :: JBang :: Plugin :: Testing 8.1s SUCCESS
Camel :: YAML DSL :: Deserializers 7.8s SUCCESS
Camel :: Catalog :: Camel Route Parser 7.4s SUCCESS
Camel :: Catalog :: Camel Report Maven Plugin 6.6s SUCCESS
Camel :: All Components Sync point 5.1s SUCCESS
Camel :: JBang :: Plugin :: Validate 4.7s SUCCESS
Camel :: YAML DSL :: Validator Maven Plugin 3.9s SUCCESS
Camel :: YAML DSL :: Maven Plugins 3.7s SUCCESS
Camel :: Catalog :: Maven 3.0s SUCCESS
Camel :: Catalog :: Suggest (deprecated) 3.0s SUCCESS
Camel :: Assembly 2.0s SUCCESS
Camel :: JBang :: Plugin :: Edit 1.5s SUCCESS
Camel :: Coverage 1.2s SUCCESS
Camel :: Catalog :: Dummy Component 1.2s SUCCESS
Camel :: Catalog :: Console 1.2s SUCCESS
Camel :: JBang :: Integration tests 1.0s SUCCESS
Camel :: JBang :: Plugin :: Generate 0.9s SUCCESS
Camel :: Endpoint DSL :: Support 0.8s SUCCESS
Camel :: Launcher :: Container 0.7s SUCCESS
Camel :: JBang :: Main 0.7s SUCCESS
Camel :: JBang :: Plugin :: MCP 0.6s SUCCESS
Camel :: JBang :: Plugin :: Route Parser 0.5s SUCCESS
Camel :: Disruptor n/a
Camel :: Endpoint DSL n/a
Camel :: Integration Tests n/a
Camel :: JBang :: Core n/a

Top 20 slowest modules:

  • Camel :: Launcher (49.4s)
  • Camel :: JBang :: MCP (39.9s)
  • Camel :: JBang :: Plugin :: TUI (37.1s)
  • Camel :: Component DSL (26.1s)
  • Camel :: Catalog :: Camel Catalog (22.7s)
  • Camel :: YAML DSL (18.7s)
  • Camel :: JBang :: Plugin :: Kubernetes (14.9s)
  • Camel :: Docs (14.9s)
  • Camel :: Kamelet Main (12.0s)
  • Camel :: YAML DSL :: Validator (10.4s)
  • Camel :: JBang :: Plugin :: Testing (8.1s)
  • Camel :: YAML DSL :: Deserializers (7.8s)
  • Camel :: Catalog :: Camel Route Parser (7.4s)
  • Camel :: Catalog :: Camel Report Maven Plugin (6.6s)
  • Camel :: All Components Sync point (5.1s)
  • Camel :: JBang :: Plugin :: Validate (4.7s)
  • Camel :: YAML DSL :: Validator Maven Plugin (3.9s)
  • Camel :: YAML DSL :: Maven Plugins (3.7s)
  • Camel :: Catalog :: Maven (3.0s)
  • Camel :: Catalog :: Suggest (deprecated) (3.0s)

⚙️ View full build and test results

@davsclaus davsclaus added this to the 4.23.0 milestone Sep 28, 2026
@davsclaus davsclaus added the bug Something isn't working label Sep 28, 2026
@davsclaus davsclaus self-assigned this Sep 28, 2026
@davsclaus
davsclaus merged commit bac8649 into main Sep 28, 2026
7 checks passed
@davsclaus
davsclaus deleted the fix/CAMEL-25082 branch September 28, 2026 20:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working components docs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants