unbridled-41 opened a new pull request, #5450:
URL: https://github.com/apache/rocketmq-dashboard/pull/5450
## Problem / Evidence
`AiRunServiceTest.stopShouldAbortALiveRunAndLeaveItStoppedWithUserStopTest`
fails intermittently in CI, on branches that do not touch this code at all:
```
[ERROR]
AiRunServiceTest.stopShouldAbortALiveRunAndLeaveItStoppedWithUserStopTest:327
Expecting Optional to contain a value but it was empty.
```
Observed on three PRs of this batch that only change unrelated areas — #5436
(LLM key persistence), #5441 (DLQ resend confinement) and #5447 (alert
threshold unit) — so it is a pre-existing flake, not a regression. The
assertion under test (pre-fix):
```java
RmqAiRun stopped = service.stop(RUN_ID);
assertThat(registry.handle(RUN_ID)) // line 326
.get() // line 327 — the failure
.satisfies(handle ->
assertThat(handle.abortReason()).contains(AbortReason.USER_STOP));
```
## Root cause
`service.stop(RUN_ID)` → `AgentRunRegistry.stop` → `AgentRunHandle.stop` →
`awaitStop()` → `cancelWorker()`, i.e. it interrupts the worker, and the
worker's own finalisation is what removes the registration:
`AiRunExecutor.execute`'s `finally` calls `registry.finish(run.getId())`
(`AiRunExecutor.java:301`), which does `runs.remove(runId)`. The test then
looks the run up **again**, so it races with that removal.
Measured on this machine with a temporary probe (removed again): the run
reaches `STOPPED`, and the registration is gone **2 ms** later. Locally the
assertion usually wins the 2 ms window (the test passed 5/5 and 10/10 here); on
a loaded CI runner it loses, which is exactly the recorded failure. Roughly one
in ten of this batch's `Backend Tests` runs was red because of it.
## Fix
Capture the handle **before** stopping and assert the reason on that
reference — the same intent, without the race:
```java
AgentRunHandle handle = registry.handle(RUN_ID).orElseThrow();
RmqAiRun stopped = service.stop(RUN_ID);
assertThat(handle.abortReason()).contains(AbortReason.USER_STOP);
```
The user-visible attribution stays asserted through the run row, unchanged:
`awaitRunStatus(STOPPED)` and `lastRun().getStopReason() == USER_STOP`.
## Priority & scoring
PRIORITY 55 = 影响 14 (an unrelated flake turns ~1 in 10 CI runs red, sending
contributors after phantom failures and blocking merges) + 波及 10 (every backend
CI run) + 可复现 11 (intermittent, mechanism demonstrated above) + 维护价值 20 (a CI
signal contributors can trust).
FIX_CONFIDENCE 85: two lines, no behavioural assertion dropped; stability
measured with 10 consecutive runs of the class.
## Tests
```
cd server && mvn -o test
-Dtest='org.apache.rocketmq.studio.ops.ai.conversation.*Test'
```
- Green: `Tests run: 201, Failures: 0, Errors: 0, Skipped: 0` (includes
`AgentRunHandleStopTest`, which still pins the handle-level single-stop
semantics).
- Stability: `AiRunServiceTest` run 10 times in a row → `Tests run: 22,
Failures: 0, Errors: 0, Skipped: 0` each time.
- Mechanism proof (temporary probe, not committed): after the run reaches
`STOPPED`, `registry.handle(RUN_ID)` is still present immediately and
disappears within 2 ms — the window the old assertion raced.
- `mvn -o checkstyle:check` passes.
## Risk
None for production code: only the test's assertion target changes. The
assertion it makes (the stop is attributed to the user, and the run ends
STOPPED/USER_STOP) is preserved.
--
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]