Skip to content

CAMEL-24126 - Ensure no message sent before the VertxWebsocket is fully configured#24740

Open
apupier wants to merge 1 commit into
apache:mainfrom
apupier:flakyVertxWebsocketTest
Open

CAMEL-24126 - Ensure no message sent before the VertxWebsocket is fully configured#24740
apupier wants to merge 1 commit into
apache:mainfrom
apupier:flakyVertxWebsocketTest

Conversation

@apupier

@apupier apupier commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

it was visible with flaky test
VertxWebsocketHandshakeHeadersTest.testHandshakeHeadersAsConsumer see rtx-websocket:surefire:test@default-test/details/org.apache.camel.component.vertx.websocket.VertxWebsocketHandshakeHeadersTest/testHandshakeHeadersAsConsumer?top-execution=1

Co-authored-by: IBM Bob IDE 2.0.1

On main branch, i reproduce the flakiness something like 1 out of 3 times. With this PR, it has never failed in 10 runs

Description

Target

  • I checked that the commit is targeting the correct branch (Camel 4 uses the main branch)

Tracking

  • If this is a large change, bug fix, or code improvement, I checked there is a JIRA issue filed for the change (usually before you start working on it).

Apache Camel coding standards and style

  • I checked that each commit in the pull request has a meaningful subject line and body.
  • I have run mvn clean install -DskipTests locally from root folder and I have committed all auto-generated changes.

AI-assisted contributions

  • If this PR includes AI-generated code, commits have proper co-authorship attribution (e.g., Co-authored-by trailers) and the PR description identifies the AI tool used.

@apupier apupier requested a review from jamesnetherton July 15, 2026 15:35
@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.

@github-actions

Copy link
Copy Markdown
Contributor

🧪 CI tested the following changed modules:

  • components/camel-vertx/camel-vertx-websocket

🔬 Scalpel shadow comparison — Scalpel: 10 tested, 29 compile-only — current: 10 all tested

Maveniverse Scalpel detected 39 affected modules (current approach: 10).

⚠️ Modules only in Scalpel (29)
  • apache-camel
  • camel-allcomponents
  • camel-catalog
  • camel-catalog-console
  • camel-catalog-lucene
  • camel-catalog-maven
  • camel-catalog-suggest
  • camel-componentdsl
  • camel-csimple-maven-plugin
  • 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
  • docs
  • dummy-component

Skip-tests mode would test 10 modules (1 direct + 9 downstream), skip tests for 29 (generated code, meta-modules)

Modules Scalpel would test (10)
  • camel-ibm-watsonx-ai
  • camel-jbang-mcp
  • camel-jbang-plugin-mcp
  • camel-jbang-plugin-route-parser
  • camel-jbang-plugin-tui
  • camel-jbang-plugin-validate
  • camel-launcher-container
  • camel-vertx-websocket
  • camel-yaml-dsl-validator
  • camel-yaml-dsl-validator-maven-plugin
Modules with tests skipped (29)
  • apache-camel
  • camel-allcomponents
  • camel-catalog
  • camel-catalog-console
  • camel-catalog-lucene
  • camel-catalog-maven
  • camel-catalog-suggest
  • camel-componentdsl
  • camel-csimple-maven-plugin
  • 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
  • docs
  • dummy-component

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

All tested modules (39 modules)
  • Camel :: All Components Sync point
  • Camel :: Assembly
  • Camel :: Catalog :: CSimple Maven Plugin (deprecated)
  • Camel :: Catalog :: Camel Catalog
  • Camel :: Catalog :: Camel Report Maven Plugin
  • Camel :: Catalog :: Camel Route Parser
  • Camel :: Catalog :: Console
  • Camel :: Catalog :: Dummy Component
  • Camel :: Catalog :: Lucene (deprecated)
  • Camel :: Catalog :: Maven
  • Camel :: Catalog :: Suggest
  • Camel :: Component DSL
  • Camel :: Coverage
  • Camel :: Docs
  • Camel :: Endpoint DSL
  • Camel :: Endpoint DSL :: Support
  • Camel :: IBM :: watsonx.ai
  • Camel :: Integration Tests
  • Camel :: JBang :: Core
  • Camel :: JBang :: Integration tests
  • Camel :: JBang :: MCP
  • Camel :: JBang :: Main
  • Camel :: JBang :: Plugin :: Edit
  • Camel :: JBang :: Plugin :: Generate
  • Camel :: JBang :: Plugin :: Kubernetes
  • Camel :: JBang :: Plugin :: MCP
  • Camel :: JBang :: Plugin :: Route Parser
  • Camel :: JBang :: Plugin :: TUI
  • Camel :: JBang :: Plugin :: Testing
  • Camel :: JBang :: Plugin :: Validate
  • Camel :: Kamelet Main
  • Camel :: Launcher
  • Camel :: Launcher :: Container
  • Camel :: Vert.x :: WebSocket
  • Camel :: YAML DSL
  • Camel :: YAML DSL :: Deserializers
  • Camel :: YAML DSL :: Maven Plugins
  • Camel :: YAML DSL :: Validator
  • Camel :: YAML DSL :: Validator Maven Plugin

⚙️ View full build and test results

@apupier apupier marked this pull request as ready for review July 15, 2026 16:14

@gnodet gnodet 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.

Well-designed race condition fix — LGTM.

What the PR does

Fixes a race where the WebSocket server could send messages immediately after the handshake completes, but before the consumer's message/close/exception handlers were registered — causing lost messages.

Before: doStart() called getWebSocket() (which blocks until connection completes), then separately called configureWebSocketHandlers(ws). A window existed between connection establishment and handler registration.

After: doStart() calls getWebSocket(this::configureWebSocketHandlers), passing handler setup as a callback. The callback is invoked inside the Vert.x onComplete block before future.complete(ws), so handlers are registered atomically with the connection — no window for lost messages.

The same fix is correctly applied to the reconnection path as well.

Design notes

  • The FQCN java.util.function.Consumer<WebSocket> in the method signature is the right call — org.apache.camel.Consumer is already imported (line 34), so the simple name Consumer would be ambiguous.
  • The Javadoc on the new getWebSocket(Consumer<WebSocket>) overload clearly explains the atomicity guarantee.
  • No test is included, which is expected — this is a timing-sensitive race condition that is not reliably reproducible in a unit test. The fix is verifiable by code inspection.

Checklist

  • Commit convention — descriptive title
  • No public API break — getWebSocket() is protected, new overload is additive
  • No security concerns
  • CI — all checks passing (JDK 17, JDK 25)

Static analysis

ast-grep (only tool available) — no findings on the modified files.


Reviewed with Claude on behalf of gnodet. This review was generated by an AI agent and may contain inaccuracies; please verify all suggestions before applying.

@davsclaus davsclaus 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.

Thanks for the contribution @apupier — nice catch on the race condition at the framework level.

The fix looks correct: registering handlers inside the onComplete callback before future.complete(ws) ensures no messages are lost between connection and handler setup. The producer path and reconnect path are both unaffected/correctly updated.

A couple of observations:

  1. JIRA ticket — Since this modifies production code (VertxWebsocketEndpoint and VertxWebsocketClientConsumer), it would be good to have a JIRA issue for tracking. The commit message would then follow the CAMEL-XXXX: Brief description convention.

  2. Minor error-handling gap — If setupCallback.accept(ws) were to throw, future.complete(ws) would never be called and the caller would get a TimeoutException instead of the real cause. In practice configureWebSocketHandlers only calls handler setters so this is low-risk, but wrapping the callback in a try/catch that calls future.completeExceptionally(e) would be more robust.

This review was generated by an AI agent (Claude Code on behalf of davsclaus) and may contain inaccuracies. Please verify all suggestions before applying.

if (setupCallback != null) {
setupCallback.accept(ws);
}
future.complete(ws);

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.

Minor robustness suggestion: if setupCallback.accept(ws) throws, future.complete(ws) is never reached and the caller gets a TimeoutException rather than the real cause. Consider:

Suggested change
future.complete(ws);
if (setupCallback != null) {
try {
setupCallback.accept(ws);
} catch (Exception e) {
future.completeExceptionally(e);
return;
}
}

Low-risk in practice since configureWebSocketHandlers only calls handler setters, but would help debugging if this pattern is reused elsewhere.

@apupier apupier force-pushed the flakyVertxWebsocketTest branch from 55c2d3c to 2734888 Compare July 16, 2026 03:24
configured

it was visible with flaky test
VertxWebsocketHandshakeHeadersTest.testHandshakeHeadersAsConsumer
see rtx-websocket:surefire:test@default-test/details/org.apache.camel.component.vertx.websocket.VertxWebsocketHandshakeHeadersTest/testHandshakeHeadersAsConsumer?top-execution=1

Co-authored-by: IBM Bob IDE 2.0.1
Signed-off-by: Aurélien Pupier <apupier@ibm.com>
@apupier apupier changed the title Ensure no message sent before the VertxWebsocket is fully configured CAMEL-24126 - Ensure no message sent before the VertxWebsocket is fully configured Jul 16, 2026
@apupier apupier force-pushed the flakyVertxWebsocketTest branch from 2734888 to 42dd1b5 Compare July 16, 2026 03:26
@apupier apupier requested a review from davsclaus July 16, 2026 03:26

@davsclaus davsclaus 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.

Thanks for fixing this race condition, @apupier — the approach of registering handlers inside the onComplete callback before future.complete() is clean and correct.

One minor formatting nit inline. Otherwise this looks good.

This review does not replace specialized AI review tools (CodeRabbit, Sourcery) or static analyzers (SonarCloud).

This review was generated by an AI agent (Claude Code on behalf of davsclaus) and may contain inaccuracies. Please verify all suggestions before applying.

setupCallback.accept(ws);
} catch(Exception e) {
future.completeExceptionally(e);
return;

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.

Minor formatting nit — missing space before the parenthesis:

Suggested change
return;
} catch (Exception e) {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants