github-actions[bot] commented on code in PR #66409:
URL: https://github.com/apache/doris/pull/66409#discussion_r3820352757


##########
fe/fe-core/src/main/java/org/apache/doris/metric/MetricRepo.java:
##########
@@ -1463,16 +1469,44 @@ public Long getValue() {
                 DORIS_METRIC_REGISTER.addMetrics(failedTaskCount);
 
                 GaugeMetric<Long> lag = new GaugeMetric<Long>(
-                        STREAMING_JOB_PER_JOB_LAG, MetricUnit.SECONDS,
-                        "per job lag in seconds of streaming job, -1 means 
N/A") {
+                        STREAMING_JOB_PER_JOB_LAG_BYTES, MetricUnit.BYTES,
+                        "latest successfully observed source log lag in bytes, 
-1 means no valid observation") {
                     @Override
                     public Long getValue() {
-                        return sJob.getLagSeconds();
+                        return sJob.getLagBytes();
                     }
                 };
                 lag.addLabel(new MetricLabel("job_id", jobId))
                         .addLabel(new MetricLabel("job_name", jobName));
                 DORIS_METRIC_REGISTER.addMetrics(lag);
+
+                long lastSourceEventTimestampSeconds = 
sJob.getLastSourceEventTimestampSeconds();
+                GaugeMetric<Long> lastSourceEventTimestamp = new 
GaugeMetric<Long>(
+                        
STREAMING_JOB_PER_JOB_LAST_SOURCE_EVENT_TIMESTAMP_SECONDS, MetricUnit.SECONDS,
+                        "timestamp of the latest source binlog or WAL event 
recorded in the job's committed offset"
+                                + " as Unix seconds, 0 means unavailable") {
+                    @Override
+                    public Long getValue() {
+                        return lastSourceEventTimestampSeconds;
+                    }
+                };
+                lastSourceEventTimestamp.addLabel(new MetricLabel("job_id", 
jobId))
+                        .addLabel(new MetricLabel("job_name", jobName));
+                DORIS_METRIC_REGISTER.addMetrics(lastSourceEventTimestamp);
+
+                long lastTaskSuccessTimeSeconds = 
sJob.getLastTaskSuccessTimeSeconds();
+                GaugeMetric<Long> lastTaskSuccessTime = new GaugeMetric<Long>(
+                        STREAMING_JOB_PER_JOB_LAST_TASK_SUCCESS_TIME_SECONDS, 
MetricUnit.SECONDS,

Review Comment:
   [P2] Persist this timestamp with the successful task transition before 
exporting it as the latest completion time. In the transaction-backed path, the 
durable commit attachment/replay advances the offset and success count but not 
`lastTaskSuccessTime`; live `onSuccess()` sets it afterward without a job 
update. In the multi-table path, `commitOffset()` writes any job update before 
`successCallback()` sets it. After failover this new gauge can therefore return 
`0` or task N-1 while the replayed offset/count include task N. Please journal 
or reconstruct the timestamp in the same durable transition and add 
immediate-failover coverage for both paths.



##########
fe/fe-core/src/main/java/org/apache/doris/job/offset/jdbc/JdbcSourceOffsetProvider.java:
##########
@@ -960,45 +997,46 @@ protected boolean isSnapshotOnlyMode() {
     }
 
     @Override
-    public String getLag() {
-        if (currentOffset == null || currentOffset.snapshotSplit()) {
-            return "";
-        }
-        // Source is idle (last task consumed no data), report zero lag
-        if (!hasMoreData) {
-            return "0";
-        }
-        BinlogSplit binlogSplit = (BinlogSplit) 
currentOffset.getSplits().get(0);
-        Map<String, String> offsetMap = binlogSplit.getStartingOffset();
-        if (MapUtils.isEmpty(offsetMap)) {
-            return "";
-        }
-        long eventTimeMs = extractEventTimeMs(offsetMap);
-        if (eventTimeMs <= 0) {
-            return "0";
-        }
-        long lagSec = (System.currentTimeMillis() - eventTimeMs) / 1000;
-        return String.valueOf(Math.max(lagSec, 0));
+    public long getLagBytes() {
+        return lagBytes;
     }
 
-    /**
-     * Extract event timestamp in milliseconds from binlog offset map.
-     * MySQL: ts_sec (seconds), PostgreSQL: ts_usec (microseconds).
-     */
-    protected long extractEventTimeMs(Map<String, String> offsetMap) {
-        try {
-            String tsSec = offsetMap.get("ts_sec");
-            if (tsSec != null) {
-                return Long.parseLong(tsSec) * 1000;
+    @Override
+    public long getLastSourceEventTimestampSeconds() {
+        synchronized (splitsLock) {
+            if (currentOffset == null || currentOffset.snapshotSplit()) {
+                return 0;
             }
-            String tsUsec = offsetMap.get("ts_usec");
-            if (tsUsec != null) {
-                return Long.parseLong(tsUsec) / 1000;
+            BinlogSplit binlogSplit = (BinlogSplit) 
currentOffset.getSplits().get(0);
+            Map<String, String> offsetMap = binlogSplit.getStartingOffset();
+            if (MapUtils.isEmpty(offsetMap)) {
+                return 0;
             }
-        } catch (NumberFormatException e) {
-            log.warn("Failed to parse event timestamp from offset: {}", 
offsetMap, e);
+            try {
+                String timestampSeconds = offsetMap.get("ts_sec");
+                if (timestampSeconds != null) {
+                    return Long.parseLong(timestampSeconds);
+                }
+                String timestampMicros = offsetMap.get("ts_usec");
+                if (timestampMicros != null) {
+                    return Long.parseLong(timestampMicros) / 1_000_000;

Review Comment:
   [P2] Normalize PostgreSQL's no-event timestamp before exporting it. An 
`offset=latest`/initial reader can commit 
[`PostgresOffset.INITIAL_OFFSET`](https://github.com/apache/flink-cdc/blob/release-3.6.0/flink-cdc-connect/flink-cdc-source-connectors/flink-connector-postgres-cdc/src/main/java/org/apache/flink/cdc/connectors/postgres/source/offset/PostgresOffset.java#L39-L59),
 whose `ts_usec` is `Conversions.toEpochMicros(Instant.MIN)` 
(`Long.MIN_VALUE`); this branch returns `-9223372036854`, and the new 
Prometheus gauge publishes it even though its contract says `0` means 
unavailable (SHOW only hides it via a separate `> 0` check). Please normalize 
the connector's unavailable sentinel to `0` and add an idle baseline PostgreSQL 
fixture using the real sentinel.



##########
fe/fe-core/src/main/java/org/apache/doris/job/extensions/insert/InsertJob.java:
##########
@@ -105,7 +105,8 @@ public class InsertJob extends AbstractJob<InsertTask, 
Map<Object, Object>> impl
             .add(new Column("LoadStatistic", ScalarType.createStringType()))
             .add(new Column("ErrorMsg", ScalarType.createStringType()))
             .add(new Column("JobRuntimeMsg", ScalarType.createStringType()))
-            .add(new Column("Lag", ScalarType.createStringType()))
+            .add(new Column("LagBytes", ScalarType.createStringType()))
+            .add(new Column("LastSourceEventTimestamp", 
ScalarType.createStringType()))

Review Comment:
   [P2] Keep this TVF schema compatible across mixed FE versions. The query FE 
builds projected names from its local `InsertJob.SCHEMA`, the BE forwards those 
names to the master, and the master resolves them with its own 
`COLUMN_TO_INDEX`. During a rolling upgrade, a new query FE sends 
`LagBytes`/`LastSourceEventTimestamp` to an old master, while an old query FE 
sends the removed `Lag` to a new master; either unknown name yields a null 
index and fails the metadata fetch. Please gate the schema by master/cluster 
compatibility or provide versioned aliases/defaults, and test both rolling 
directions.



-- 
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]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to