-
Notifications
You must be signed in to change notification settings - Fork 5.2k
CAMEL-23129: Fix CountDownLatch count mismatch in ThreadPerTaskSedaConsumer #25281
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
davsclaus
merged 1 commit into
apache:main
from
gnodet:seda-threadpertasksedaconsumer-fix-countdownlatch
Aug 4, 2026
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,18 +16,22 @@ | |
| */ | ||
| package org.apache.camel.component.seda; | ||
|
|
||
| import java.util.concurrent.TimeUnit; | ||
|
|
||
| import org.apache.camel.ContextTestSupport; | ||
| import org.apache.camel.builder.RouteBuilder; | ||
| import org.apache.camel.component.mock.MockEndpoint; | ||
| import org.junit.jupiter.api.Test; | ||
|
|
||
| import static org.junit.jupiter.api.Assertions.assertTrue; | ||
|
|
||
| /** | ||
| * Test for the virtualThreadPerTask mode of SEDA consumer | ||
| */ | ||
| public class ThreadPerTaskSedaConsumerTest extends ContextTestSupport { | ||
| class ThreadPerTaskSedaConsumerTest extends ContextTestSupport { | ||
|
|
||
| @Test | ||
| public void testVirtualThreadPerTask() throws Exception { | ||
| void testVirtualThreadPerTask() throws Exception { | ||
| MockEndpoint mock = getMockEndpoint("mock:result"); | ||
| mock.expectedMessageCount(10); | ||
|
|
||
|
|
@@ -39,7 +43,7 @@ public void testVirtualThreadPerTask() throws Exception { | |
| } | ||
|
|
||
| @Test | ||
| public void testVirtualThreadPerTaskWithConcurrencyLimit() throws Exception { | ||
| void testVirtualThreadPerTaskWithConcurrencyLimit() throws Exception { | ||
| MockEndpoint mock = getMockEndpoint("mock:limited"); | ||
| mock.expectedMessageCount(5); | ||
|
|
||
|
|
@@ -51,7 +55,7 @@ public void testVirtualThreadPerTaskWithConcurrencyLimit() throws Exception { | |
| } | ||
|
|
||
| @Test | ||
| public void testVirtualThreadPerTaskHighThroughput() throws Exception { | ||
| void testVirtualThreadPerTaskHighThroughput() throws Exception { | ||
| int messageCount = 100; | ||
| MockEndpoint mock = getMockEndpoint("mock:throughput"); | ||
| mock.expectedMessageCount(messageCount); | ||
|
|
@@ -63,6 +67,32 @@ public void testVirtualThreadPerTaskHighThroughput() throws Exception { | |
| mock.assertIsSatisfied(); | ||
| } | ||
|
|
||
| @Test | ||
| void testShutdownWithConcurrencyLimitCompletesQuickly() throws Exception { | ||
| // Send messages so the route is actively used | ||
| for (int i = 0; i < 5; i++) { | ||
| template.sendBody("seda:limited?virtualThreadPerTask=true&concurrentConsumers=2", "Message " + i); | ||
| } | ||
|
|
||
| MockEndpoint mock = getMockEndpoint("mock:limited"); | ||
| mock.expectedMessageCount(5); | ||
| mock.assertIsSatisfied(); | ||
|
|
||
| // Stop the context and verify it completes quickly. | ||
| // Before the fix, the CountDownLatch was initialized with concurrentConsumers | ||
| // count (2) but only 1 coordinator thread counts down, so prepareShutdown() | ||
| // would wait the full shutdown timeout before proceeding. | ||
| long start = System.nanoTime(); | ||
| context.stop(); | ||
| long elapsed = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - start); | ||
|
|
||
| // Shutdown should complete well within the default timeout (300s). | ||
| // Use a generous 30s bound to avoid flakiness, but this is still much less | ||
| // than the full shutdown strategy timeout that would be hit without the fix. | ||
| assertTrue(elapsed < 30, "Context stop took " + elapsed + "s, expected < 30s. " | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Convention nit: Per project conventions, new test code should use AssertJ assertions. Consider: assertThat(elapsed)
.as("Context stop took %ds; CountDownLatch count likely mismatches coordinator thread count", elapsed)
.isLessThan(30L);This provides better failure messages and aligns with the project's preference for AssertJ over JUnit assertions. |
||
| + "The CountDownLatch count likely does not match the coordinator thread count."); | ||
| } | ||
|
|
||
| @Override | ||
| protected RouteBuilder createRouteBuilder() { | ||
| return new RouteBuilder() { | ||
|
|
||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Design nit: Changing
private→protectedon thelatchfield exposes mutable internal state to all subclasses. A template method would achieve the same goal without field exposure:Then
ThreadPerTaskSedaConsumerjust overridesgetLatchCount()to return1— no need to touchlatchdirectly, and it eliminates the "create then immediately replace" pattern wheresuper.doStart()allocates aCountDownLatch(N)that is discarded one line later.Non-blocking — the current approach is functionally correct.