From 98d22f899dd44eb9f9b3e9d6321ef0ce09c08718 Mon Sep 17 00:00:00 2001 From: bobhan1 Date: Wed, 17 Jun 2026 14:47:11 +0800 Subject: [PATCH] [fix](fe) Show warm-up timestamps with date ### What problem does this PR solve? Issue Number: None Related PR: None Problem Summary: Event-driven warm-up detailed SyncStats serialized last_trigger_ts, last_finish_ts, and progress_trigger_ts with only HH:mm:ss. When a job spans dates, the output cannot identify which day the event happened. BE reports these values as epoch milliseconds, so FE can render them directly as yyyy-MM-dd HH:mm:ss while preserving empty strings for unset timestamps. ### Release note SHOW WARM UP JOB detailed SyncStats now displays warm-up timestamp fields with full date and time. ### Check List (For Author) - Test: Unit Test - ./run-fe-ut.sh --run org.apache.doris.cloud.WarmUpStatsTest,org.apache.doris.cloud.CloudWarmUpJobTableFilterTest - Behavior changed: Yes. Detailed warm-up timestamp strings now include the date. - Does this need documentation: No --- .../apache/doris/cloud/JobWarmUpStats.java | 4 +-- .../cloud/CloudWarmUpJobTableFilterTest.java | 5 +++ .../apache/doris/cloud/WarmUpStatsTest.java | 33 +++++++++++++++++++ 3 files changed, 40 insertions(+), 2 deletions(-) diff --git a/fe/fe-core/src/main/java/org/apache/doris/cloud/JobWarmUpStats.java b/fe/fe-core/src/main/java/org/apache/doris/cloud/JobWarmUpStats.java index cdb293216a1b54..6d44b904741cca 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/cloud/JobWarmUpStats.java +++ b/fe/fe-core/src/main/java/org/apache/doris/cloud/JobWarmUpStats.java @@ -32,7 +32,7 @@ * across all matched tables, then computes gap = requested - finished. */ public class JobWarmUpStats { - private static final DateTimeFormatter TIME_FMT = DateTimeFormatter.ofPattern("HH:mm:ss"); + private static final DateTimeFormatter DATETIME_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); // Aggregated requested public long requestedSegmentNum5m; @@ -277,7 +277,7 @@ private static String formatEpochMs(long epochMs) { } try { return LocalDateTime.ofInstant(Instant.ofEpochMilli(epochMs), ZoneId.systemDefault()) - .format(TIME_FMT); + .format(DATETIME_FMT); } catch (Exception e) { return ""; } diff --git a/fe/fe-core/src/test/java/org/apache/doris/cloud/CloudWarmUpJobTableFilterTest.java b/fe/fe-core/src/test/java/org/apache/doris/cloud/CloudWarmUpJobTableFilterTest.java index 1af6bf284db221..44e3771eba9418 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/cloud/CloudWarmUpJobTableFilterTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/cloud/CloudWarmUpJobTableFilterTest.java @@ -365,6 +365,7 @@ public void testGetJobInfoClusterLevelEventDrivenJobShowsSyncStats() { stats.finishIndexSize30m = 512; stats.failSegmentNum30m = 1; stats.lastTriggerTs = 5000; + stats.lastFinishTs = 4800; stats.progressTriggerTs = 4200; stats.computeGap(); @@ -380,6 +381,10 @@ public void testGetJobInfoClusterLevelEventDrivenJobShowsSyncStats() { Assertions.assertEquals(4, segNum.get("finish_30m").getAsLong()); Assertions.assertEquals(2, segNum.get("gap_30m").getAsLong()); Assertions.assertEquals(800, detailStats.get("trigger_gap_ms").getAsLong()); + String dateTimePattern = "\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}"; + Assertions.assertTrue(detailStats.get("last_trigger_ts").getAsString().matches(dateTimePattern)); + Assertions.assertTrue(detailStats.get("last_finish_ts").getAsString().matches(dateTimePattern)); + Assertions.assertTrue(detailStats.get("progress_trigger_ts").getAsString().matches(dateTimePattern)); Assertions.assertFalse(detailStats.has("window")); List summary = job.getJobInfo(stats, false); diff --git a/fe/fe-core/src/test/java/org/apache/doris/cloud/WarmUpStatsTest.java b/fe/fe-core/src/test/java/org/apache/doris/cloud/WarmUpStatsTest.java index 2f8c35f73941f0..573bd115b8396b 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/cloud/WarmUpStatsTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/cloud/WarmUpStatsTest.java @@ -22,6 +22,10 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; import java.util.HashMap; import java.util.Map; @@ -31,6 +35,7 @@ * - JobWarmUpStats: aggregate requested/finished, compute gap, serialize */ public class WarmUpStatsTest { + private static final DateTimeFormatter DATETIME_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); // ==================== TableWarmUpWindowedStats ==================== @@ -313,6 +318,29 @@ public void testToJsonStringZeroTimestamps() { Assertions.assertEquals(0, segNum.get("gap_5m").getAsLong()); } + @Test + public void testToJsonStringFormatsTimestampsWithDate() { + long lastTriggerTs = 1700000000000L; + long lastFinishTs = 1700000001000L; + long progressTriggerTs = 1699999999000L; + + JobWarmUpStats job = new JobWarmUpStats(); + job.lastTriggerTs = lastTriggerTs; + job.lastFinishTs = lastFinishTs; + job.progressTriggerTs = progressTriggerTs; + + JsonObject root = JsonParser.parseString(job.toJsonString()).getAsJsonObject(); + + Assertions.assertEquals(formatExpectedDateTime(lastTriggerTs), + root.get("last_trigger_ts").getAsString()); + Assertions.assertEquals(formatExpectedDateTime(lastFinishTs), + root.get("last_finish_ts").getAsString()); + Assertions.assertEquals(formatExpectedDateTime(progressTriggerTs), + root.get("progress_trigger_ts").getAsString()); + Assertions.assertTrue(root.get("last_trigger_ts").getAsString() + .matches("\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}")); + } + @Test public void testToSummaryJsonStringMergesDataAndIndexSize() { JobWarmUpStats job = new JobWarmUpStats(); @@ -494,4 +522,9 @@ public void testClusterLevelEventDrivenJobAggregatesStatsByJobId() { Assertions.assertEquals(1000, stats.lastTriggerTs); Assertions.assertEquals(1200, stats.lastFinishTs); } + + private static String formatExpectedDateTime(long epochMs) { + return LocalDateTime.ofInstant(Instant.ofEpochMilli(epochMs), ZoneId.systemDefault()) + .format(DATETIME_FMT); + } }