Croway commented on code in PR #29741:
URL: https://github.com/apache/camel/pull/29741#discussion_r4554344406


##########
components/camel-ai/camel-mcp-server-api/src/test/java/org/apache/camel/component/mcp/server/conformance/McpServerConformanceTestSupport.java:
##########
@@ -102,7 +102,7 @@ public void configure() {
 
                 from("ai-tool:slow_tool?tags=" + CONFORMANCE_TAG + 
"&description=Exceeds the tool timeout")
                         .routeId("slow-tool-route")
-                        .delay(TOOL_TIMEOUT_MILLIS * 3)
+                        .delay(TOOL_TIMEOUT_MILLIS + 200)

Review Comment:
   `TOOL_TIMEOUT_MILLIS + 200` leaves only 200 ms between the 2 s `future.get` 
timeout in `McpServerBridge` and the route completing (it was 4 s). If the 
waiting thread wakes more than ~200 ms late, `get` returns `done` and 
`testCallToolTimeout` fails. A GC pause or a busy CI runner can cause that, and 
with this PR the test classes also run concurrently. I could not make it flake 
locally, so this is a risk, not a reproduced failure.
   
   There is a variant that keeps the 4 s margin and is also faster: keep `* 3` 
and make the delay synchronous.
   
   ```java
   .delay(TOOL_TIMEOUT_MILLIS * 3).syncDelayed()
   ```
   
   On timeout the bridge already calls `future.cancel(true)`. With a 
synchronous delay, that interrupt ends the exchange at once 
(`DelayProcessorSupport` lines 106-111), so nothing is inflight when the 
context stops. With `+ 200` the exchange is still inflight at shutdown. 
Graceful shutdown checks for inflight exchanges once per second 
(`DefaultShutdownStrategy` line 739), so it still costs about 1 s per class.
   
   Measured on the PR head with `-Dcamel.surefire.parallel=false` (3 runs each, 
main 1 run); all tests pass in every variant:
   
   | `slow_tool` / `slow_resource` delay | `VertxMcpServerConformanceTest` | 
`VertxMcpServerResourceConformanceTest` |
   |---|---|---|
   | main: `* 3` | 7.7 s | 6.5 s |
   | this PR: `+ 200` | 4.6–4.7 s | 3.5 s |
   | `* 3` + `.syncDelayed()` | 3.5–3.7 s | 2.5 s |
   
   The same change applies to `slow_resource` in 
`McpServerResourceConformanceTestSupport` line 113.



##########
components/camel-ai/camel-mcp-server/src/test/java/org/apache/camel/component/mcp/server/vertx/VertxMcpSessionEvictionTest.java:
##########
@@ -49,60 +50,68 @@ class VertxMcpSessionEvictionTest {
 
     @Test
     void evictsIdleSessionWithoutDelete() throws Exception {
-        int port = AvailablePortFinder.getNextAvailable();
-        VertxMcpServerEngine engine = startEngine(port, new 
McpServerInfo("eviction", "1.0", "/mcp", 0, 500));
-        try {
-            initializeSession(port);
-            assertThat(engine.sessionCount()).isEqualTo(1);
-
-            await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> 
assertThat(engine.sessionCount()).isZero());
-        } finally {
-            engine.stop();
+        try (Port portHolder = AvailablePortFinder.find()) {
+            int port = portHolder.getPort();
+            VertxMcpServerEngine engine = startEngine(port, new 
McpServerInfo("eviction", "1.0", "/mcp", 0, 500));
+            try {
+                initializeSession(port);
+                assertThat(engine.sessionCount()).isEqualTo(1);
+
+                await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> 
assertThat(engine.sessionCount()).isZero());
+            } finally {
+                engine.stop();
+            }
         }
     }
 
     @Test
     void postOnlySessionSurvivesKeepAliveUntilIdle() throws Exception {
-        int port = AvailablePortFinder.getNextAvailable();
-        VertxMcpServerEngine engine = startEngine(port, new 
McpServerInfo("eviction", "1.0", "/mcp", 300, 10_000));
-        try {
-            String sessionId = initializeSession(port);
-
-            await().during(2, TimeUnit.SECONDS).atMost(3, 
TimeUnit.SECONDS).untilAsserted(() -> {
-                assertThat(postWithSession(port, sessionId, 
INITIALIZED_NOTIFICATION)).isEqualTo(202);
-                assertThat(engine.sessionCount()).isEqualTo(1);
-            });
-        } finally {
-            engine.stop();
+        try (Port portHolder = AvailablePortFinder.find()) {
+            int port = portHolder.getPort();
+            VertxMcpServerEngine engine = startEngine(port, new 
McpServerInfo("eviction", "1.0", "/mcp", 300, 10_000));
+            try {
+                String sessionId = initializeSession(port);
+
+                await().during(500, TimeUnit.MILLISECONDS).atMost(1500, 
TimeUnit.MILLISECONDS).untilAsserted(() -> {
+                    assertThat(postWithSession(port, sessionId, 
INITIALIZED_NOTIFICATION)).isEqualTo(202);
+                    assertThat(engine.sessionCount()).isEqualTo(1);
+                });
+            } finally {
+                engine.stop();
+            }
         }
     }
 
     @Test
     void activeSessionSurvivesIdleEviction() throws Exception {
-        int port = AvailablePortFinder.getNextAvailable();
-        VertxMcpServerEngine engine = startEngine(port, new 
McpServerInfo("eviction", "1.0", "/mcp", 0, 2_000));
-        try {
-            String sessionId = initializeSession(port);
-
-            await().during(2, TimeUnit.SECONDS).atMost(3, 
TimeUnit.SECONDS).untilAsserted(() -> {
-                assertThat(postWithSession(port, sessionId, 
INITIALIZED_NOTIFICATION)).isEqualTo(202);
-                assertThat(engine.sessionCount()).isEqualTo(1);
-            });
-        } finally {
-            engine.stop();
+        try (Port portHolder = AvailablePortFinder.find()) {
+            int port = portHolder.getPort();
+            VertxMcpServerEngine engine = startEngine(port, new 
McpServerInfo("eviction", "1.0", "/mcp", 0, 2_000));
+            try {
+                String sessionId = initializeSession(port);
+
+                await().during(500, TimeUnit.MILLISECONDS).atMost(1500, 
TimeUnit.MILLISECONDS).untilAsserted(() -> {

Review Comment:
   With the 500 ms window this test can no longer fail. The idle TTL is 2 s and 
the eviction scan runs every `min(ttl / 2, 1000)` = 1 s, so a session that 
nobody touches is still alive when the window closes, about 0.6 s after 
`initialize`.
   
   I checked it with a mutation that removes `managed.touch()` from the POST 
path (`VertxMcpStreamableServerTransportProvider` line 472), so client activity 
no longer keeps the session alive:
   
   | Variant | Without the mutation | With the mutation |
   |---|---|---|
   | this PR: TTL 2 s, `during(500 ms)` | 5/5 pass (~0.67 s) | **5/5 pass** 
(the real test too, 3/3) |
   | main: TTL 2 s, `during(2 s)` | 5/5 pass (~2.15 s) | 1/5 fail |
   | suggested: TTL 500 ms, `during(1500 ms)` | 5/5 pass (~1.65 s) | **5/5 
fail** |
   
   I suggest shrinking the TTL rather than the window. With a 500 ms TTL the 
scan runs every 250 ms, so an untouched session is gone within ~750 ms, well 
inside the window. The TTL is still 5× the 100 ms polling interval, so active 
sessions are safe:
   
   ```java
   VertxMcpServerEngine engine = startEngine(port, new 
McpServerInfo("eviction", "1.0", "/mcp", 0, 500));
   // ...
   await().during(1500, TimeUnit.MILLISECONDS).atMost(2500, 
TimeUnit.MILLISECONDS).untilAsserted(() -> {
   ```
   
   That is ~1 s slower than this PR but still faster than main, and the test 
checks the behaviour again.



##########
components/camel-ai/camel-mcp-server/pom.xml:
##########
@@ -144,6 +148,29 @@
 
     </dependencies>
 
+    <build>

Review Comment:
   The PR description says failsafe does not read the parallel property. In 
fact `components/pom.xml` (profile `full`, lines 577-581) already sets 
`junit.jupiter.execution.parallel.*` for failsafe from 
`camel.failsafe.parallel`, and camel-ftp, camel-jms and camel-mina-sftp only 
set that property. `configurationParameters` also take precedence over system 
properties, so this block makes `-Dcamel.failsafe.parallel=false` a no-op. That 
flag is useful to rule out parallelism when an IT flakes on CI.
   
   I checked it with a probe IT that prints the thread JUnit runs it on. A 
`ForkJoinPool-…` thread means parallel and `main` means serial:
   
   | pom | `-Dcamel.failsafe.parallel=false` | Thread | Forked JVM 
`junit.jupiter.execution.parallel.enabled` |
   |---|---|---|---|
   | this PR | – | `ForkJoinPool-1-worker-1` | `false` |
   | this PR | yes | `ForkJoinPool-1-worker-1` | `false` |
   | suggested (below) | – | `ForkJoinPool-1-worker-1` | `true` |
   | suggested (below) | yes | `main` | `false` |
   | main | – | `main` | `false` |
   
   I suggest dropping the whole `<build>` block and setting the property next 
to the surefire one:
   
   ```xml
   <camel.surefire.parallel>true</camel.surefire.parallel>
   <camel.failsafe.parallel>true</camel.failsafe.parallel>
   ```
   
   That uses the failsafe default factor of 0.5. Add 
`<camel.failsafe.parallel.factor>1</camel.failsafe.parallel.factor>` if you 
want 1. Separately, `camel.surefire.parallel.factor=1` on line 43 is already 
the default in `parent/pom.xml` (line 44), so that line can go.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to