This is an automated email from the ASF dual-hosted git repository.
lizhimins pushed a commit to branch rocketmq-studio
in repository https://gitbox.apache.org/repos/asf/rocketmq-dashboard.git
The following commit(s) were added to refs/heads/rocketmq-studio by this push:
new b1561bde4 fix(ai): stamp gmt_modified when reporting run speed (#7048)
b1561bde4 is described below
commit b1561bde45204578032f84dbb4cfd550c1827b0d
Author: wizcraft_kris <[email protected]>
AuthorDate: Mon Oct 26 16:28:43 2026 +0800
fix(ai): stamp gmt_modified when reporting run speed (#7048)
---
.../rocketmq/studio/ops/ai/conversation/AiRunService.java | 9 +++++++--
.../rocketmq/studio/ops/ai/conversation/AiRunServiceTest.java | 10 +++++++---
.../rocketmq/studio/ops/ai/conversation/AiRunTestSupport.java | 1 +
3 files changed, 15 insertions(+), 5 deletions(-)
diff --git
a/server/src/main/java/org/apache/rocketmq/studio/ops/ai/conversation/AiRunService.java
b/server/src/main/java/org/apache/rocketmq/studio/ops/ai/conversation/AiRunService.java
index 0d8787516..4f279ab0a 100644
---
a/server/src/main/java/org/apache/rocketmq/studio/ops/ai/conversation/AiRunService.java
+++
b/server/src/main/java/org/apache/rocketmq/studio/ops/ai/conversation/AiRunService.java
@@ -477,8 +477,12 @@ public class AiRunService {
* the non-null columns the stale read still carries — {@code status},
{@code end_seq} and
* {@code gmt_modified} — are written back over the terminal ones. The
nullable terminal facts
* (duration, tokens, finished_at) survive: MyBatis-Plus defaults to
{@code FieldStrategy.NOT_NULL},
- * so a null field never reaches the SET clause. Only the speed column is
touched, the same
- * partial-update shape {@code rememberRuntimeSession} uses.
+ * so a null field never reaches the SET clause. The write touches only
the speed column and the
+ * bookkeeping timestamp, the same partial-update shape {@code
rememberRuntimeSession} uses.
+ * {@code gmt_modified} is stamped explicitly, from the same service clock
as every other write
+ * to the row: the column is declared {@code ON UPDATE CURRENT_TIMESTAMP},
so leaving it null
+ * would not leave it alone — the database would rewrite it in its session
timezone, a different
+ * clock than the UTC cutoff the orphan sweep compares against.
*
* @throws BusinessException 404 when the run does not exist or belongs to
somebody else
*/
@@ -488,6 +492,7 @@ public class AiRunService {
RmqAiRun update = new RmqAiRun();
update.setId(run.getId());
update.setTokensPerSecond(tokensPerSecond);
+ update.setGmtModified(LocalDateTime.now(clock));
runRepository.update(update);
run.setTokensPerSecond(tokensPerSecond);
return run;
diff --git
a/server/src/test/java/org/apache/rocketmq/studio/ops/ai/conversation/AiRunServiceTest.java
b/server/src/test/java/org/apache/rocketmq/studio/ops/ai/conversation/AiRunServiceTest.java
index 1aaf16866..bd35285ad 100644
---
a/server/src/test/java/org/apache/rocketmq/studio/ops/ai/conversation/AiRunServiceTest.java
+++
b/server/src/test/java/org/apache/rocketmq/studio/ops/ai/conversation/AiRunServiceTest.java
@@ -43,6 +43,7 @@ import
org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
+import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.List;
@@ -439,12 +440,15 @@ class AiRunServiceTest {
}
@Test
- void reportSpeedShouldTouchOnlyTheSpeedColumnTest() {
+ void reportSpeedShouldStampGmtModifiedWithoutTouchingFinalizeColumnsTest()
{
// The client reports the speed as its stream closes, which also
happens mid-run when the
// connection drops while the run keeps executing. A full-row write
read before the worker
// finalises would race finalizeRun's terminal update and write the
stale RUNNING state back
// over it, resurrecting the run as active and locking the
conversation with 409s until the
- // orphan sweep reaps the row. The update must therefore carry only
the id and the speed.
+ // orphan sweep reaps the row. The update must therefore carry only
the id, the speed and
+ // the bookkeeping timestamp, stamped from the service clock — an
unstamped partial update
+ // lets the DDL's ON UPDATE CURRENT_TIMESTAMP rewrite gmt_modified in
the database session's
+ // timezone, which is not the clock the orphan cutoff is compared
against.
RmqAiRun running = AiRunTestSupport.run(RUN_ID, CONVERSATION_ID, 1,
RunStatus.RUNNING);
when(runRepository.findById(RUN_ID)).thenReturn(Optional.of(running));
@@ -462,7 +466,7 @@ class AiRunServiceTest {
assertThat(update.getEndSeq()).isNull();
assertThat(update.getInputTokens()).isNull();
assertThat(update.getOutputTokens()).isNull();
- assertThat(update.getGmtModified()).isNull();
+
assertThat(update.getGmtModified()).isEqualTo(LocalDateTime.parse("2026-09-18T10:00:00"));
}
@Test
diff --git
a/server/src/test/java/org/apache/rocketmq/studio/ops/ai/conversation/AiRunTestSupport.java
b/server/src/test/java/org/apache/rocketmq/studio/ops/ai/conversation/AiRunTestSupport.java
index 412c33101..b0a2ee519 100644
---
a/server/src/test/java/org/apache/rocketmq/studio/ops/ai/conversation/AiRunTestSupport.java
+++
b/server/src/test/java/org/apache/rocketmq/studio/ops/ai/conversation/AiRunTestSupport.java
@@ -362,6 +362,7 @@ final class AiRunTestSupport {
copy.setErrorCode(run.getErrorCode());
copy.setErrorMessage(run.getErrorMessage());
copy.setTokensPerSecond(run.getTokensPerSecond());
+ copy.setGmtModified(run.getGmtModified());
return copy;
}