fix(managed-agent): gate the pinned-scheduler witness lane on no -Dtest filter (#13401)

This commit is contained in:
qwen-code-dev-bot 2026-10-05 17:08:27 +00:00
parent d2a39fd5ee
commit 5eab7d39b1
4 changed files with 125 additions and 64 deletions

View file

@ -113,29 +113,6 @@
<failIfNoTests>true</failIfNoTests>
<excludedGroups>integration</excludedGroups>
</configuration>
<executions>
<!-- Re-run the pinning witness with a scheduler
parallelism that differs from the processor count:
no other lane sets the property, so the carrier
sizing is otherwise unobservable. The property is
read once in VirtualThread.<clinit>, so it must
arrive on the fork's argLine; @{argLine} keeps the
JaCoCo agent prepare-agent sets. -->
<execution>
<id>pinning-witnesses-pinned-scheduler</id>
<phase>test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration combine.self="override">
<failIfNoTests>true</failIfNoTests>
<argLine>@{argLine} -Djdk.virtualThreadScheduler.parallelism=4</argLine>
<includes>
<include>**/HarnessEventStreamPinningTest.java</include>
</includes>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
@ -232,6 +209,57 @@
</plugins>
</build>
<profiles>
<profile>
<!-- Surefire's test user property replaces the includes of
every execution of the plugin, so with this execution
inline a -Dtest= caller (scripts/run-java-daemon-sdk-e2e.ts
runs the daemon E2E that way) would re-run its narrowed
class in the pinned fork while the witness below never
runs. Keep the lane in a profile that exists only while no
-Dtest filter is in play. -->
<id>pinning-witness-lane</id>
<activation>
<property>
<name>!test</name>
</property>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
<executions>
<!-- Re-run the pinning witness with a scheduler
parallelism that differs from the processor
count: no other lane sets the property, so
the carrier sizing is otherwise
unobservable. The property is read once in
VirtualThread.<clinit>, so it must arrive on
the fork's argLine; @{argLine} keeps the
JaCoCo agent prepare-agent sets. -->
<execution>
<id>pinning-witnesses-pinned-scheduler</id>
<phase>test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration combine.self="override">
<failIfNoTests>true</failIfNoTests>
<argLine>@{argLine} -Djdk.virtualThreadScheduler.parallelism=4</argLine>
<includes>
<include>**/HarnessEventStreamPinningTest.java</include>
</includes>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<organization>
<name>Alibaba Group</name>
<url>https://github.com/alibaba</url>

View file

@ -220,11 +220,14 @@ class HarnessEventStreamPinningTest {
if (configured == null) {
return Runtime.getRuntime().availableProcessors();
}
// The JDK's own read is a bare Integer.parseInt, once, in
// VirtualThread.createDefaultScheduler: no trim, no catch, no
// clamp. A malformed value kills scheduler init there before any
// The JDK's own read of parallelism is a bare Integer.parseInt,
// once, in VirtualThread.createDefaultScheduler: no trim, no
// catch. A malformed value kills scheduler init there before any
// witness can run, so leniency here would only mis-size fleets in
// a JVM that cannot start a virtual thread at all.
// a JVM that cannot start a virtual thread at all. This models
// parallelism alone: the JDK also clamps it down to
// jdk.virtualThreadScheduler.maxPoolSize when that is set, so an
// externally capped pool makes this read too high.
return Integer.parseInt(configured);
}

View file

@ -91,33 +91,6 @@
<failIfNoTests>true</failIfNoTests>
<excludedGroups>fault-gate</excludedGroups>
</configuration>
<executions>
<!-- Re-run the pinning witnesses with a scheduler
parallelism that differs from the processor count:
no other lane sets the property, so the
carrier-sized arrival latches are otherwise
unobservable. The property is read once in
VirtualThread.<clinit>, so it must arrive on the
fork's argLine; @{argLine} keeps the JaCoCo agent
prepare-agent sets. combine.self=override keeps
the fault-gates profile's groups filter from
emptying this execution. -->
<execution>
<id>pinning-witnesses-pinned-scheduler</id>
<phase>test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration combine.self="override">
<failIfNoTests>true</failIfNoTests>
<argLine>@{argLine} -Djdk.virtualThreadScheduler.parallelism=4</argLine>
<includes>
<include>**/BrokerVirtualThreadPinningTest.java</include>
<include>**/BrokerRenewalPinningTest.java</include>
</includes>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
@ -169,6 +142,57 @@
</build>
<profiles>
<profile>
<!-- Surefire's test user property replaces the includes of
every execution of the plugin, so with this execution
inline a -Dtest= caller would re-run its narrowed class in
the pinned fork while the witnesses below never run. Keep
the lane in a profile that exists only while no -Dtest
filter is in play. -->
<id>pinning-witness-lane</id>
<activation>
<property>
<name>!test</name>
</property>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
<executions>
<!-- Re-run the pinning witnesses with a scheduler
parallelism that differs from the processor
count: no other lane sets the property, so
the carrier-sized arrival latches are
otherwise unobservable. The property is read
once in VirtualThread.<clinit>, so it must
arrive on the fork's argLine; @{argLine}
keeps the JaCoCo agent prepare-agent sets.
combine.self=override keeps the fault-gates
profile's groups filter from emptying this
execution. -->
<execution>
<id>pinning-witnesses-pinned-scheduler</id>
<phase>test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration combine.self="override">
<failIfNoTests>true</failIfNoTests>
<argLine>@{argLine} -Djdk.virtualThreadScheduler.parallelism=4</argLine>
<includes>
<include>**/BrokerVirtualThreadPinningTest.java</include>
<include>**/BrokerRenewalPinningTest.java</include>
</includes>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<!-- Stage F multi-process fault gates. They need the bundled CLI
(npm run build && npm run bundle at the repository root),

View file

@ -1,12 +1,15 @@
package com.alibaba.qwen.code.runtimebroker;
/**
* Resolves the virtual-thread scheduler's carrier count the way the JDK
* does: {@code jdk.virtualThreadScheduler.parallelism} parsed base-10,
* falling back to the processor count. {@code Integer.getInteger} resolves
* through {@code Integer.decode}, so a value like {@code 0100} would read
* as 64 while the JVM builds 100 carriers — every pinning witness must
* size its fleet from this one read or a misspelled property falsifies it.
* Resolves the virtual-thread scheduler's carrier count from the JDK's
* parallelism read: {@code jdk.virtualThreadScheduler.parallelism} parsed
* base-10, falling back to the processor count. {@code Integer.getInteger}
* resolves through {@code Integer.decode}, so a value like {@code 0100}
* would read as 64 while the JVM builds 100 carriers — every pinning
* witness must size its fleet from this one read or a misspelled property
* falsifies it. This models the parallelism read alone: the JDK also
* clamps it down to {@code jdk.virtualThreadScheduler.maxPoolSize} when
* that is set, so an externally capped pool makes this read too high.
*/
final class CarrierCount {
private CarrierCount() {
@ -18,11 +21,14 @@ final class CarrierCount {
if (configured == null) {
return Runtime.getRuntime().availableProcessors();
}
// The JDK's own read is a bare Integer.parseInt, once, in
// VirtualThread.createDefaultScheduler: no trim, no catch, no
// clamp. A malformed value kills scheduler init there before any
// The JDK's own read of parallelism is a bare Integer.parseInt,
// once, in VirtualThread.createDefaultScheduler: no trim, no
// catch. A malformed value kills scheduler init there before any
// witness can run, so leniency here would only mis-size fleets in
// a JVM that cannot start a virtual thread at all.
// a JVM that cannot start a virtual thread at all. This models
// parallelism alone: the JDK also clamps it down to
// jdk.virtualThreadScheduler.maxPoolSize when that is set, so an
// externally capped pool makes this read too high.
return Integer.parseInt(configured);
}
}