This is an automated email from the ASF dual-hosted git repository.

loserwang1024 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/flink-cdc.git


The following commit(s) were added to refs/heads/master by this push:
     new b11c961ad [FLINK-38907][postgres] Fix data loss with filtered 
publication autocreate mode in backfill phase (#4223)
b11c961ad is described below

commit b11c961adecc6095b9bd97ff63df0bea4b22a1fa
Author: Giovanny GutiĆ©rrez <[email protected]>
AuthorDate: Wed Oct 14 01:05:06 2026 -0500

    [FLINK-38907][postgres] Fix data loss with filtered publication autocreate 
mode in backfill phase (#4223)
---
 .../fetch/PostgresSourceFetchTaskContext.java      |  7 ++
 .../postgres/source/PostgresSourceITCase.java      | 88 +++++++++++++++++++++-
 .../source/fetch/PostgresScanFetchTaskTest.java    | 20 +++--
 .../source/reader/PostgresSourceReaderTest.java    |  2 +
 .../postgres/testutils/UniqueDatabase.java         |  9 +++
 .../src/test/resources/ddl/customer.sql            | 31 ++++++++
 6 files changed, 145 insertions(+), 12 deletions(-)

diff --git 
a/flink-cdc-connect/flink-cdc-source-connectors/flink-connector-postgres-cdc/src/main/java/org/apache/flink/cdc/connectors/postgres/source/fetch/PostgresSourceFetchTaskContext.java
 
b/flink-cdc-connect/flink-cdc-source-connectors/flink-connector-postgres-cdc/src/main/java/org/apache/flink/cdc/connectors/postgres/source/fetch/PostgresSourceFetchTaskContext.java
index ff94ecfcc..b518546d6 100644
--- 
a/flink-cdc-connect/flink-cdc-source-connectors/flink-connector-postgres-cdc/src/main/java/org/apache/flink/cdc/connectors/postgres/source/fetch/PostgresSourceFetchTaskContext.java
+++ 
b/flink-cdc-connect/flink-cdc-source-connectors/flink-connector-postgres-cdc/src/main/java/org/apache/flink/cdc/connectors/postgres/source/fetch/PostgresSourceFetchTaskContext.java
@@ -76,6 +76,7 @@ import static 
io.debezium.connector.AbstractSourceInfo.SCHEMA_NAME_KEY;
 import static io.debezium.connector.AbstractSourceInfo.TABLE_NAME_KEY;
 import static 
io.debezium.connector.postgresql.PostgresConnectorConfig.DROP_SLOT_ON_STOP;
 import static 
io.debezium.connector.postgresql.PostgresConnectorConfig.PLUGIN_NAME;
+import static 
io.debezium.connector.postgresql.PostgresConnectorConfig.PUBLICATION_AUTOCREATE_MODE;
 import static 
io.debezium.connector.postgresql.PostgresConnectorConfig.SLOT_NAME;
 import static 
io.debezium.connector.postgresql.PostgresConnectorConfig.SNAPSHOT_MODE;
 import static 
io.debezium.connector.postgresql.PostgresObjectUtils.createReplicationConnection;
@@ -144,6 +145,12 @@ public class PostgresSourceFetchTaskContext extends 
JdbcSourceFetchTaskContext {
                                     .with(DROP_SLOT_ON_STOP.name(), true)
                                     // Disable heartbeat event in snapshot 
split fetcher
                                     .with(Heartbeat.HEARTBEAT_INTERVAL, 0)
+                                    // Never touch the shared publication in 
backfill tasks;
+                                    // filtered mode would shrink it to this 
split's single table
+                                    // and other tables' changes would be 
dropped by pgoutput
+                                    .with(
+                                            PUBLICATION_AUTOCREATE_MODE.name(),
+                                            
PostgresConnectorConfig.AutoCreateMode.DISABLED)
                                     .build());
         } else {
 
diff --git 
a/flink-cdc-connect/flink-cdc-source-connectors/flink-connector-postgres-cdc/src/test/java/org/apache/flink/cdc/connectors/postgres/source/PostgresSourceITCase.java
 
b/flink-cdc-connect/flink-cdc-source-connectors/flink-connector-postgres-cdc/src/test/java/org/apache/flink/cdc/connectors/postgres/source/PostgresSourceITCase.java
index d884d079e..95c15d775 100644
--- 
a/flink-cdc-connect/flink-cdc-source-connectors/flink-connector-postgres-cdc/src/test/java/org/apache/flink/cdc/connectors/postgres/source/PostgresSourceITCase.java
+++ 
b/flink-cdc-connect/flink-cdc-source-connectors/flink-connector-postgres-cdc/src/test/java/org/apache/flink/cdc/connectors/postgres/source/PostgresSourceITCase.java
@@ -74,6 +74,7 @@ import java.util.concurrent.atomic.AtomicLong;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 
+import static 
io.debezium.connector.postgresql.connection.ReplicationConnection.Builder.DEFAULT_PUBLICATION_NAME;
 import static java.lang.String.format;
 import static 
org.apache.flink.cdc.connectors.postgres.testutils.PostgresTestUtils.hasNextData;
 import static 
org.apache.flink.cdc.connectors.postgres.testutils.PostgresTestUtils.triggerFailover;
@@ -185,6 +186,24 @@ class PostgresSourceITCase extends PostgresTestBase {
                 scanStartupMode);
     }
 
+    @Test
+    void testFilteredPublication() throws Exception {
+        Map<String, String> options = new HashMap<>();
+        options.put("debezium.publication.autocreate.mode", "filtered");
+        options.put("scan.incremental.snapshot.chunk.size", "10");
+        testPostgresParallelSource(
+                1,
+                DEFAULT_SCAN_STARTUP_MODE,
+                PostgresTestUtils.FailoverType.NONE,
+                PostgresTestUtils.FailoverPhase.NEVER,
+                new String[] {"Customers", "customers_1", "customers_2"},
+                1,
+                0,
+                options,
+                this::checkPublicationForBackfillPhase,
+                false);
+    }
+
     @ParameterizedTest
     @ValueSource(strings = {"initial", "latest-offset"})
     void testReadMultipleTableWithMultipleParallelism(String scanStartupMode) 
throws Exception {
@@ -905,6 +924,31 @@ class PostgresSourceITCase extends PostgresTestBase {
             Map<String, String> otherOptions,
             StreamDataChecker streamDataChecker)
             throws Exception {
+        testPostgresParallelSource(
+                parallelism,
+                scanStartupMode,
+                failoverType,
+                failoverPhase,
+                captureCustomerTables,
+                restartAttempts,
+                delayBetweenAttempts,
+                otherOptions,
+                streamDataChecker,
+                true);
+    }
+
+    private void testPostgresParallelSource(
+            int parallelism,
+            String scanStartupMode,
+            PostgresTestUtils.FailoverType failoverType,
+            PostgresTestUtils.FailoverPhase failoverPhase,
+            String[] captureCustomerTables,
+            int restartAttempts,
+            long delayBetweenAttempts,
+            Map<String, String> otherOptions,
+            StreamDataChecker streamDataChecker,
+            boolean checkSnapshot)
+            throws Exception {
         StreamExecutionEnvironment env = 
StreamExecutionEnvironment.getExecutionEnvironment();
         StreamTableEnvironment tEnv = StreamTableEnvironment.create(env);
 
@@ -917,7 +961,7 @@ class PostgresSourceITCase extends PostgresTestBase {
         TableResult tableResult = tEnv.executeSql("select * from customers");
 
         // first step: check the snapshot data
-        if (DEFAULT_SCAN_STARTUP_MODE.equals(scanStartupMode)) {
+        if (checkSnapshot && 
DEFAULT_SCAN_STARTUP_MODE.equals(scanStartupMode)) {
             checkSnapshotData(tableResult, failoverType, failoverPhase, 
captureCustomerTables);
         }
 
@@ -1036,6 +1080,48 @@ class PostgresSourceITCase extends PostgresTestBase {
                 expectedSnapshotData, fetchRows(iterator, 
expectedSnapshotData.size()));
     }
 
+    private void checkPublicationForBackfillPhase(
+            TableResult tableResult,
+            PostgresTestUtils.FailoverType failoverType,
+            PostgresTestUtils.FailoverPhase failoverPhase,
+            String[] captureCustomerTables)
+            throws Exception {
+        List<String> expectedTables =
+                Arrays.stream(captureCustomerTables)
+                        .map(table -> SCHEMA_NAME + "." + table)
+                        .collect(Collectors.toList());
+        // Backfill creates its slot after initializing the shared publication.
+        String query =
+                format(
+                        "SELECT schemaname, tablename FROM 
pg_publication_tables "
+                                + "WHERE pubname = '%s' AND EXISTS ("
+                                + "SELECT 1 FROM pg_replication_slots "
+                                + "WHERE slot_name = '%s_0' AND database = 
current_database())",
+                        DEFAULT_PUBLICATION_NAME, slotName);
+        long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(30);
+        try (PostgresConnection connection = getConnection()) {
+            while (System.nanoTime() < deadline) {
+                List<String> publishedTables = new ArrayList<>();
+                connection.query(
+                        query,
+                        rs -> {
+                            while (rs.next()) {
+                                publishedTables.add(rs.getString(1) + "." + 
rs.getString(2));
+                            }
+                        });
+                connection.commit();
+                if (!publishedTables.isEmpty()) {
+                    assertThat(publishedTables)
+                            .as("Publication must retain all captured tables 
during backfill")
+                            
.containsExactlyInAnyOrderElementsOf(expectedTables);
+                    return;
+                }
+                Thread.sleep(10L);
+            }
+        }
+        throw new AssertionError("No backfill publication observed within 30 
seconds");
+    }
+
     private void checkStreamData(
             TableResult tableResult,
             PostgresTestUtils.FailoverType failoverType,
diff --git 
a/flink-cdc-connect/flink-cdc-source-connectors/flink-connector-postgres-cdc/src/test/java/org/apache/flink/cdc/connectors/postgres/source/fetch/PostgresScanFetchTaskTest.java
 
b/flink-cdc-connect/flink-cdc-source-connectors/flink-connector-postgres-cdc/src/test/java/org/apache/flink/cdc/connectors/postgres/source/fetch/PostgresScanFetchTaskTest.java
index eb9834b17..edceb8197 100644
--- 
a/flink-cdc-connect/flink-cdc-source-connectors/flink-connector-postgres-cdc/src/test/java/org/apache/flink/cdc/connectors/postgres/source/fetch/PostgresScanFetchTaskTest.java
+++ 
b/flink-cdc-connect/flink-cdc-source-connectors/flink-connector-postgres-cdc/src/test/java/org/apache/flink/cdc/connectors/postgres/source/fetch/PostgresScanFetchTaskTest.java
@@ -45,10 +45,12 @@ import org.apache.flink.table.types.DataType;
 
 import io.debezium.connector.postgresql.connection.PostgresConnection;
 import org.apache.kafka.connect.source.SourceRecord;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
+import java.sql.SQLException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -56,6 +58,7 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Optional;
 
+import static 
io.debezium.connector.postgresql.connection.ReplicationConnection.Builder.DEFAULT_PUBLICATION_NAME;
 import static org.assertj.core.api.Assertions.assertThat;
 
 /** Tests for {@link PostgresScanFetchTask}. */
@@ -75,10 +78,14 @@ class PostgresScanFetchTaskTest extends PostgresTestBase {
                     POSTGRES_CONTAINER.getUsername(),
                     POSTGRES_CONTAINER.getPassword());
 
-    @Test
-    void testChangingDataInSnapshotScan() throws Exception {
+    @BeforeEach
+    void before() throws SQLException {
         customDatabase.createAndInitialize();
+        customDatabase.createPublication(DEFAULT_PUBLICATION_NAME);
+    }
 
+    @Test
+    void testChangingDataInSnapshotScan() throws Exception {
         TestTableId tableId = new TestTableId(schemaName, tableName);
         String[] changingDataSql =
                 new String[] {
@@ -112,7 +119,6 @@ class PostgresScanFetchTaskTest extends PostgresTestBase {
 
     @Test
     void testInsertDataInSnapshotScan() throws Exception {
-        customDatabase.createAndInitialize();
         TestTableId tableId = new TestTableId(schemaName, tableName);
         String[] insertDataSql =
                 new String[] {
@@ -145,7 +151,6 @@ class PostgresScanFetchTaskTest extends PostgresTestBase {
 
     @Test
     void testDeleteDataInSnapshotScan() throws Exception {
-        customDatabase.createAndInitialize();
         TestTableId tableId = new TestTableId(schemaName, tableName);
         String[] deleteDataSql =
                 new String[] {
@@ -170,8 +175,6 @@ class PostgresScanFetchTaskTest extends PostgresTestBase {
 
     @Test
     void testSnapshotScanSkipBackfillWithPostLowWatermark() throws Exception {
-        customDatabase.createAndInitialize();
-
         TestTableId tableId = new TestTableId(schemaName, tableName);
         String[] changingDataSql =
                 new String[] {
@@ -207,8 +210,6 @@ class PostgresScanFetchTaskTest extends PostgresTestBase {
 
     @Test
     void testSnapshotScanSkipBackfillWithPreHighWatermark() throws Exception {
-        customDatabase.createAndInitialize();
-
         TestTableId tableId = new TestTableId(schemaName, tableName);
         String[] changingDataSql =
                 new String[] {
@@ -244,8 +245,6 @@ class PostgresScanFetchTaskTest extends PostgresTestBase {
 
     @Test
     void testMultipleSplitsWithBackfill() throws Exception {
-        customDatabase.createAndInitialize();
-
         TestTableId tableId = new TestTableId(schemaName, tableName);
         PostgresSourceConfigFactory sourceConfigFactory =
                 getMockPostgresSourceConfigFactory(
@@ -295,7 +294,6 @@ class PostgresScanFetchTaskTest extends PostgresTestBase {
 
     @Test
     void testSnapshotFetchSize() throws Exception {
-        customDatabase.createAndInitialize();
         PostgresSourceConfigFactory sourceConfigFactory =
                 getMockPostgresSourceConfigFactory(
                         customDatabase, schemaName, tableName, null, 10, true);
diff --git 
a/flink-cdc-connect/flink-cdc-source-connectors/flink-connector-postgres-cdc/src/test/java/org/apache/flink/cdc/connectors/postgres/source/reader/PostgresSourceReaderTest.java
 
b/flink-cdc-connect/flink-cdc-source-connectors/flink-connector-postgres-cdc/src/test/java/org/apache/flink/cdc/connectors/postgres/source/reader/PostgresSourceReaderTest.java
index c8ead39f8..10ba00328 100644
--- 
a/flink-cdc-connect/flink-cdc-source-connectors/flink-connector-postgres-cdc/src/test/java/org/apache/flink/cdc/connectors/postgres/source/reader/PostgresSourceReaderTest.java
+++ 
b/flink-cdc-connect/flink-cdc-source-connectors/flink-connector-postgres-cdc/src/test/java/org/apache/flink/cdc/connectors/postgres/source/reader/PostgresSourceReaderTest.java
@@ -61,6 +61,7 @@ import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
+import static 
io.debezium.connector.postgresql.connection.ReplicationConnection.Builder.DEFAULT_PUBLICATION_NAME;
 import static org.apache.flink.core.io.InputStatus.END_OF_INPUT;
 import static org.apache.flink.core.io.InputStatus.MORE_AVAILABLE;
 import static org.assertj.core.api.Assertions.assertThat;
@@ -81,6 +82,7 @@ public class PostgresSourceReaderTest extends 
PostgresTestBase {
     @BeforeEach
     public void before() throws SQLException {
         customDatabase.createAndInitialize();
+        customDatabase.createPublication(DEFAULT_PUBLICATION_NAME);
 
         this.slotName = getSlotName();
     }
diff --git 
a/flink-cdc-connect/flink-cdc-source-connectors/flink-connector-postgres-cdc/src/test/java/org/apache/flink/cdc/connectors/postgres/testutils/UniqueDatabase.java
 
b/flink-cdc-connect/flink-cdc-source-connectors/flink-connector-postgres-cdc/src/test/java/org/apache/flink/cdc/connectors/postgres/testutils/UniqueDatabase.java
index fb46b6d70..23c736031 100644
--- 
a/flink-cdc-connect/flink-cdc-source-connectors/flink-connector-postgres-cdc/src/test/java/org/apache/flink/cdc/connectors/postgres/testutils/UniqueDatabase.java
+++ 
b/flink-cdc-connect/flink-cdc-source-connectors/flink-connector-postgres-cdc/src/test/java/org/apache/flink/cdc/connectors/postgres/testutils/UniqueDatabase.java
@@ -165,6 +165,15 @@ public class UniqueDatabase {
         }
     }
 
+    /** Creates a publication for tests that bypass the source enumerator. */
+    public void createPublication(String publicationName) throws SQLException {
+        try (Connection connection = 
PostgresTestBase.getJdbcConnection(container, databaseName);
+                Statement statement = connection.createStatement()) {
+            statement.execute(
+                    String.format("CREATE PUBLICATION %s FOR ALL TABLES", 
publicationName));
+        }
+    }
+
     /** Drop the database if it is existing. */
     public void dropDatabase() {
         try {
diff --git 
a/flink-cdc-connect/flink-cdc-source-connectors/flink-connector-postgres-cdc/src/test/resources/ddl/customer.sql
 
b/flink-cdc-connect/flink-cdc-source-connectors/flink-connector-postgres-cdc/src/test/resources/ddl/customer.sql
index 3626589aa..5a0db448c 100644
--- 
a/flink-cdc-connect/flink-cdc-source-connectors/flink-connector-postgres-cdc/src/test/resources/ddl/customer.sql
+++ 
b/flink-cdc-connect/flink-cdc-source-connectors/flink-connector-postgres-cdc/src/test/resources/ddl/customer.sql
@@ -81,6 +81,37 @@ VALUES (101,'user_1','Shanghai','123567891234'),
        (1019,'user_20','Shanghai','123567891234'),
        (2000,'user_21','Shanghai','123567891234');
 
+CREATE TABLE customers_2 (
+  "Id" INTEGER NOT NULL PRIMARY KEY,
+  "Name" VARCHAR(255) NOT NULL DEFAULT 'flink',
+  address VARCHAR(1024),
+  phone_number VARCHAR(512)
+);
+ALTER TABLE customers_2 REPLICA IDENTITY FULL;
+
+INSERT INTO customers_2
+VALUES (101,'user_1','Shanghai','123567891234'),
+       (102,'user_2','Shanghai','123567891234'),
+       (103,'user_3','Shanghai','123567891234'),
+       (109,'user_4','Shanghai','123567891234'),
+       (110,'user_5','Shanghai','123567891234'),
+       (111,'user_6','Shanghai','123567891234'),
+       (118,'user_7','Shanghai','123567891234'),
+       (121,'user_8','Shanghai','123567891234'),
+       (123,'user_9','Shanghai','123567891234'),
+       (1009,'user_10','Shanghai','123567891234'),
+       (1010,'user_11','Shanghai','123567891234'),
+       (1011,'user_12','Shanghai','123567891234'),
+       (1012,'user_13','Shanghai','123567891234'),
+       (1013,'user_14','Shanghai','123567891234'),
+       (1014,'user_15','Shanghai','123567891234'),
+       (1015,'user_16','Shanghai','123567891234'),
+       (1016,'user_17','Shanghai','123567891234'),
+       (1017,'user_18','Shanghai','123567891234'),
+       (1018,'user_19','Shanghai','123567891234'),
+       (1019,'user_20','Shanghai','123567891234'),
+       (2000,'user_21','Shanghai','123567891234');
+
 CREATE TABLE customers_no_pk (
    "Id" INTEGER NOT NULL,
    "Name" VARCHAR(255) NOT NULL DEFAULT 'flink',

Reply via email to