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


##########
fe/fe-core/src/main/java/org/apache/doris/job/extensions/insert/streaming/StreamingInsertJob.java:
##########
@@ -1095,22 +1100,25 @@ private void modifyPropertiesInternal(Map<String, 
String> inputProperties) throw
         StreamingJobProperties inputStreamProps = new 
StreamingJobProperties(inputProperties);
         if (StringUtils.isNotEmpty(inputStreamProps.getOffsetProperty())) {
             Offset offset = 
validateOffset(inputStreamProps.getOffsetProperty());
+            if (Config.isCloudMode()) {
+                resetCloudProgress(offset);

Review Comment:
   [P1] Keep combined SQL+offset ALTER atomic. The grammar allows PROPERTIES 
together with a new DML statement, but by the time this cloud reset runs 
alterJob has already installed the new executeSql/baseCommand/originTvfProps. A 
non-OK response or RpcException returns before the provider/properties change 
and before JobManager writes the edit log, so the failed ALTER leaves only the 
current master using the new SQL with the old offset; failover restores the old 
SQL. Please stage the SQL-derived state until the fallible reset succeeds (or 
restore it on failure), and cover a combined ALTER whose reset fails.



##########
fe/fe-core/src/main/java/org/apache/doris/job/offset/jdbc/JdbcSourceOffsetProvider.java:
##########
@@ -526,13 +526,46 @@ public Offset deserializeOffsetProperty(String offset) {
     }
 
     @Override
-    public void validateAlterOffset(String offset) throws Exception {
-        if (!DataSourceConfigValidator.isJsonOffset(offset)) {
+    public void validateAlterOffset(String offset) throws AnalysisException {
+        JsonNode offsetNode;
+        try {
+            offsetNode = objectMapper.readTree(offset);
+        } catch (Exception e) {
+            offsetNode = null;
+        }
+        if (offsetNode == null || !offsetNode.isObject()) {
             throw new AnalysisException(
                     "ALTER JOB for CDC only supports JSON specific offset, "
                     + "e.g. '{\"file\":\"binlog.000001\",\"pos\":\"154\"}' for 
MySQL "
                     + "or '{\"lsn\":\"12345678\"}' for PostgreSQL");
         }
+
+        boolean valid = switch (sourceType) {
+            case POSTGRES -> isNonNegativeLong(offsetNode.get("lsn"));
+            case MYSQL, OCEANBASE -> offsetNode.has("file") && 
offsetNode.has("pos")
+                    ? hasText(offsetNode.path("file")) && 
isNonNegativeLong(offsetNode.get("pos"))
+                    : hasText(offsetNode.path("gtids"));
+            default -> throw new AnalysisException(

Review Comment:
   [P1] Validate the complete file/position offset with the same contract the 
CDC reader consumes. For example, 
{"file":"mysql-bin.000001","pos":"154","kind":"BOGUS"} passes this arm because 
only file and pos are checked. MySqlSourceReader retains the full map and calls 
getOffsetKind() while selecting the next split start, so ALTER succeeds and is 
journaled, then the resumed task fails on the invalid enum value. Please reject 
invalid retained fields or share the source-specific downstream parser, with a 
negative test for an invalid kind.



##########
fs_brokers/cdc_client/src/main/java/org/apache/doris/cdcclient/source/reader/postgres/PostgresSourceReader.java:
##########
@@ -560,7 +560,7 @@ protected void validateStreamSource(
                                         + " committed position without data 
loss.",
                                 baseReq.getJobId(), dialect.getSlotName()));
             }
-            Lsn requestedLsn = extractRequestedLsn(offsetMeta);
+            Lsn requestedLsn = ((PostgresOffset) startingOffset).getLsn();
             Lsn restartLsn = slotState.slotRestartLsn();
             // restart_lsn must stay <= committed position; a higher one means 
the slot was
             // recreated

Review Comment:
   [P0] Compare against confirmed_flush_lsn, not only restart_lsn. restart_lsn 
is the oldest WAL the slot may still need and can remain behind both positions; 
the same-name slot can therefore satisfy restart_lsn <= requested while 
confirmed_flush_lsn > requested. PostgreSQL starts logical replication at the 
greater of the requested LSN and confirmed_flush_lsn, so this guard passes and 
silently skips the entire gap. Use slotLastFlushedLsn() as the consumed 
boundary (and keep any restart_lsn check needed for older-server failure 
classification), then make the regression assert the restart_lsn <= checkpoint 
< confirmed_flush_lsn case. See 
https://www.postgresql.org/docs/17/protocol-replication.html



##########
fe/fe-core/src/main/java/org/apache/doris/job/offset/jdbc/JdbcSourceOffsetProvider.java:
##########
@@ -526,13 +526,46 @@ public Offset deserializeOffsetProperty(String offset) {
     }
 
     @Override
-    public void validateAlterOffset(String offset) throws Exception {
-        if (!DataSourceConfigValidator.isJsonOffset(offset)) {
+    public void validateAlterOffset(String offset) throws AnalysisException {
+        JsonNode offsetNode;
+        try {
+            offsetNode = objectMapper.readTree(offset);
+        } catch (Exception e) {
+            offsetNode = null;
+        }
+        if (offsetNode == null || !offsetNode.isObject()) {
             throw new AnalysisException(
                     "ALTER JOB for CDC only supports JSON specific offset, "
                     + "e.g. '{\"file\":\"binlog.000001\",\"pos\":\"154\"}' for 
MySQL "
                     + "or '{\"lsn\":\"12345678\"}' for PostgreSQL");
         }
+
+        boolean valid = switch (sourceType) {
+            case POSTGRES -> isNonNegativeLong(offsetNode.get("lsn"));
+            case MYSQL, OCEANBASE -> offsetNode.has("file") && 
offsetNode.has("pos")
+                    ? hasText(offsetNode.path("file")) && 
isNonNegativeLong(offsetNode.get("pos"))
+                    : hasText(offsetNode.path("gtids"));
+            default -> throw new AnalysisException(
+                    "Unsupported CDC source type for ALTER JOB offset: " + 
sourceType);
+        };

Review Comment:
   [P1] Make the accepted GTID-only form reach the split reader. This branch 
accepts a bare {"gtids":...} map and FE persists/sends it, but 
MySqlSourceReader.createBinlogSplit only injects kind=SPECIFIC when a file key 
exists. The bare map consequently has null kind/file and the reader falls back 
to the unchanged startup offset (or earliest), silently ignoring the ALTER; 
meanwhile the normalized ofGtidSet map with kind plus empty file/pos 
placeholders is rejected by the file/position arm here. Normalize and validate 
one representation consistently in both paths, and add an end-to-end 
MySQL/OceanBase ALTER-resume test.



##########
fe/fe-core/src/main/java/org/apache/doris/job/offset/jdbc/JdbcSourceOffsetProvider.java:
##########
@@ -526,13 +526,46 @@ public Offset deserializeOffsetProperty(String offset) {
     }
 
     @Override
-    public void validateAlterOffset(String offset) throws Exception {
-        if (!DataSourceConfigValidator.isJsonOffset(offset)) {
+    public void validateAlterOffset(String offset) throws AnalysisException {
+        JsonNode offsetNode;
+        try {
+            offsetNode = objectMapper.readTree(offset);
+        } catch (Exception e) {
+            offsetNode = null;
+        }
+        if (offsetNode == null || !offsetNode.isObject()) {
             throw new AnalysisException(
                     "ALTER JOB for CDC only supports JSON specific offset, "
                     + "e.g. '{\"file\":\"binlog.000001\",\"pos\":\"154\"}' for 
MySQL "
                     + "or '{\"lsn\":\"12345678\"}' for PostgreSQL");
         }
+
+        boolean valid = switch (sourceType) {
+            case POSTGRES -> isNonNegativeLong(offsetNode.get("lsn"));
+            case MYSQL, OCEANBASE -> offsetNode.has("file") && 
offsetNode.has("pos")
+                    ? hasText(offsetNode.path("file")) && 
isNonNegativeLong(offsetNode.get("pos"))
+                    : hasText(offsetNode.path("gtids"));

Review Comment:
   [P1] Reject zero for an ALTERed PostgreSQL LSN. Here 0 is accepted, but 
PostgresSourceReader treats it as the internal initial sentinel and 
deliberately skips the slot-position guard for requestedLsn <= 0. This ALTER 
path installs a specific stream offset, so a user-requested rewind to 0 can 
instead resume at the slot's later confirmed position without reporting the 
skipped range. Keep zero for internal initial startup only and require a 
positive LSN here.



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