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

ruanhang1993 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 91040dece [FLINK-39824][flink-connector-debezium] Cache relational 
table filter results (#4422)
91040dece is described below

commit 91040dece074c18640a58f74a6391cf7613f8063
Author: Ran Tao <[email protected]>
AuthorDate: Wed Aug 12 14:32:35 2026 +0800

    [FLINK-39824][flink-connector-debezium] Cache relational table filter 
results (#4422)
    
    
    Introduce a bounded CachedTableFilter in flink-connector-debezium and
    use it as the default relational table filter.
---
 .../io/debezium/relational/CachedTableFilter.java  |  71 +++++++++++++
 .../relational/RelationalTableFilters.java         |   9 +-
 .../debezium/relational/CachedTableFilterTest.java | 112 +++++++++++++++++++++
 .../relational/RelationalTableFiltersTest.java     |  49 +++++++++
 .../mysql/source/config/MySqlSourceConfig.java     |  19 ++--
 .../mysql/source/config/MySqlSourceConfigTest.java |  54 ++++++++++
 6 files changed, 303 insertions(+), 11 deletions(-)

diff --git 
a/flink-cdc-connect/flink-cdc-source-connectors/flink-connector-debezium/src/main/java/io/debezium/relational/CachedTableFilter.java
 
b/flink-cdc-connect/flink-cdc-source-connectors/flink-connector-debezium/src/main/java/io/debezium/relational/CachedTableFilter.java
new file mode 100644
index 000000000..18e9ef93a
--- /dev/null
+++ 
b/flink-cdc-connect/flink-cdc-source-connectors/flink-connector-debezium/src/main/java/io/debezium/relational/CachedTableFilter.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.debezium.relational;
+
+import org.apache.flink.shaded.guava31.com.google.common.cache.CacheBuilder;
+import org.apache.flink.shaded.guava31.com.google.common.cache.CacheLoader;
+import org.apache.flink.shaded.guava31.com.google.common.cache.LoadingCache;
+
+import io.debezium.relational.Tables.TableFilter;
+
+import static org.apache.flink.util.Preconditions.checkNotNull;
+
+/** A bounded cache for table filter results. */
+public class CachedTableFilter implements TableFilter {
+
+    private static final long TABLE_FILTER_CACHE_MAXIMUM_SIZE = 32 * 1024;
+
+    private final TableFilter rawTableFilter;
+    private final LoadingCache<TableId, Boolean> tableFilterCache;
+
+    private CachedTableFilter(TableFilter rawTableFilter) {
+        this.rawTableFilter = rawTableFilter;
+        this.tableFilterCache =
+                CacheBuilder.newBuilder()
+                        .maximumSize(TABLE_FILTER_CACHE_MAXIMUM_SIZE)
+                        .build(
+                                new CacheLoader<TableId, Boolean>() {
+                                    @Override
+                                    public Boolean load(TableId tableId) {
+                                        return 
rawTableFilter.isIncluded(tableId);
+                                    }
+                                });
+    }
+
+    /** Wraps the given filter in a cache, or returns it unchanged if it is 
already cached. */
+    public static CachedTableFilter from(TableFilter tableFilter) {
+        checkNotNull(tableFilter);
+        if (tableFilter instanceof CachedTableFilter) {
+            return (CachedTableFilter) tableFilter;
+        }
+        return new CachedTableFilter(tableFilter);
+    }
+
+    /** Returns a new cached filter that also requires the additional filter 
to match. */
+    public CachedTableFilter withAdditionalFilter(TableFilter 
additionalFilter) {
+        checkNotNull(additionalFilter);
+        TableFilter rawFilter = rawTableFilter;
+        return new CachedTableFilter(
+                tableId -> rawFilter.isIncluded(tableId) && 
additionalFilter.isIncluded(tableId));
+    }
+
+    @Override
+    public boolean isIncluded(TableId tableId) {
+        return tableFilterCache.getUnchecked(tableId);
+    }
+}
diff --git 
a/flink-cdc-connect/flink-cdc-source-connectors/flink-connector-debezium/src/main/java/io/debezium/relational/RelationalTableFilters.java
 
b/flink-cdc-connect/flink-cdc-source-connectors/flink-connector-debezium/src/main/java/io/debezium/relational/RelationalTableFilters.java
index e36c936d5..4d91ff83c 100644
--- 
a/flink-cdc-connect/flink-cdc-source-connectors/flink-connector-debezium/src/main/java/io/debezium/relational/RelationalTableFilters.java
+++ 
b/flink-cdc-connect/flink-cdc-source-connectors/flink-connector-debezium/src/main/java/io/debezium/relational/RelationalTableFilters.java
@@ -20,7 +20,9 @@ import static 
io.debezium.relational.RelationalDatabaseConnectorConfig.COLUMN_EX
 /**
  * Copied from Debezium 1.9.8.Final.
  *
- * <p>Line 146: add a method to update the tableFilter variable.
+ * <p>Line 98: cache table filter results.
+ *
+ * <p>Line 148: add a method to update the tableFilter variable.
  */
 public class RelationalTableFilters implements DataCollectionFilters {
 
@@ -92,7 +94,8 @@ public class RelationalTableFilters implements 
DataCollectionFilters {
                         ? tablePredicate.and(systemTablesFilter::isIncluded)
                         : tablePredicate;
 
-        this.tableFilter = finalTablePredicate::test;
+        TableFilter initialTableFilter = finalTablePredicate::test;
+        this.tableFilter = CachedTableFilter.from(initialTableFilter);
 
         // Define the database filter using the include and exclude lists for 
database names ...
         this.databaseFilter =
@@ -114,7 +117,7 @@ public class RelationalTableFilters implements 
DataCollectionFilters {
 
         this.schemaSnapshotFilter =
                 
config.getBoolean(DatabaseHistory.STORE_ONLY_CAPTURED_TABLES_DDL)
-                        ? 
eligibleSchemaPredicate.and(tableFilter::isIncluded)::test
+                        ? 
eligibleSchemaPredicate.and(initialTableFilter::isIncluded)::test
                         : eligibleSchemaPredicate::test;
 
         this.excludeColumns =
diff --git 
a/flink-cdc-connect/flink-cdc-source-connectors/flink-connector-debezium/src/test/java/io/debezium/relational/CachedTableFilterTest.java
 
b/flink-cdc-connect/flink-cdc-source-connectors/flink-connector-debezium/src/test/java/io/debezium/relational/CachedTableFilterTest.java
new file mode 100644
index 000000000..7b4f65c3e
--- /dev/null
+++ 
b/flink-cdc-connect/flink-cdc-source-connectors/flink-connector-debezium/src/test/java/io/debezium/relational/CachedTableFilterTest.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.debezium.relational;
+
+import io.debezium.relational.Tables.TableFilter;
+import org.junit.jupiter.api.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class CachedTableFilterTest {
+
+    @Test
+    void testCachesPositiveAndNegativeResultsForEquivalentTableIds() {
+        AtomicInteger invocationCount = new AtomicInteger();
+        CachedTableFilter cachedTableFilter =
+                CachedTableFilter.from(
+                        tableId -> {
+                            invocationCount.incrementAndGet();
+                            return tableId.table().startsWith("orders");
+                        });
+
+        TableId includedTable = new TableId("test_db", null, "orders_1");
+        TableId sameIncludedTable = new TableId("test_db", null, "orders_1");
+        TableId unmatchedTable = new TableId("test_db", null, "customers");
+        TableId sameUnmatchedTable = new TableId("test_db", null, "customers");
+
+        assertThat(cachedTableFilter.isIncluded(includedTable)).isTrue();
+        assertThat(cachedTableFilter.isIncluded(sameIncludedTable)).isTrue();
+        assertThat(cachedTableFilter.isIncluded(unmatchedTable)).isFalse();
+        assertThat(cachedTableFilter.isIncluded(sameUnmatchedTable)).isFalse();
+        assertThat(invocationCount).hasValue(2);
+    }
+
+    @Test
+    void testFromReturnsCachedFilterUnchanged() {
+        CachedTableFilter cachedTableFilter = CachedTableFilter.from(tableId 
-> true);
+
+        
assertThat(CachedTableFilter.from(cachedTableFilter)).isSameAs(cachedTableFilter);
+    }
+
+    @Test
+    void testAdditionalFilterCreatesSingleCacheOverRawDelegate() {
+        AtomicInteger rawFilterInvocationCount = new AtomicInteger();
+        AtomicInteger additionalFilterInvocationCount = new AtomicInteger();
+        CachedTableFilter cachedTableFilter =
+                CachedTableFilter.from(
+                        tableId -> {
+                            rawFilterInvocationCount.incrementAndGet();
+                            return true;
+                        });
+        TableId tableId = new TableId("test_db", null, "orders_1");
+
+        assertThat(cachedTableFilter.isIncluded(tableId)).isTrue();
+
+        CachedTableFilter combinedFilter =
+                cachedTableFilter.withAdditionalFilter(
+                        ignored -> {
+                            additionalFilterInvocationCount.incrementAndGet();
+                            return false;
+                        });
+
+        assertThat(combinedFilter).isNotSameAs(cachedTableFilter);
+        assertThat(combinedFilter.isIncluded(tableId)).isFalse();
+        assertThat(combinedFilter.isIncluded(tableId)).isFalse();
+        assertThat(rawFilterInvocationCount).hasValue(2);
+        assertThat(additionalFilterInvocationCount).hasValue(1);
+
+        assertThat(cachedTableFilter.isIncluded(tableId)).isTrue();
+        assertThat(rawFilterInvocationCount).hasValue(2);
+    }
+
+    @Test
+    void testCacheSizeIsBounded() {
+        AtomicInteger invocationCount = new AtomicInteger();
+        int numberOfTableIds = 32 * 1024 + 1;
+        TableFilter cachedTableFilter =
+                CachedTableFilter.from(
+                        tableId -> {
+                            invocationCount.incrementAndGet();
+                            return true;
+                        });
+        List<TableId> tableIds = new ArrayList<>();
+        for (int i = 0; i < numberOfTableIds; i++) {
+            tableIds.add(new TableId("test_db", null, "table_" + i));
+        }
+
+        tableIds.forEach(cachedTableFilter::isIncluded);
+        assertThat(invocationCount).hasValue(numberOfTableIds);
+
+        tableIds.forEach(cachedTableFilter::isIncluded);
+        assertThat(invocationCount).hasValueGreaterThan(numberOfTableIds);
+    }
+}
diff --git 
a/flink-cdc-connect/flink-cdc-source-connectors/flink-connector-debezium/src/test/java/io/debezium/relational/RelationalTableFiltersTest.java
 
b/flink-cdc-connect/flink-cdc-source-connectors/flink-connector-debezium/src/test/java/io/debezium/relational/RelationalTableFiltersTest.java
new file mode 100644
index 000000000..836bc9122
--- /dev/null
+++ 
b/flink-cdc-connect/flink-cdc-source-connectors/flink-connector-debezium/src/test/java/io/debezium/relational/RelationalTableFiltersTest.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package io.debezium.relational;
+
+import io.debezium.config.Configuration;
+import io.debezium.relational.Tables.TableFilter;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class RelationalTableFiltersTest {
+
+    @Test
+    void testInitialTableFilterIsCached() {
+        RelationalTableFilters tableFilters = createTableFilters();
+
+        
assertThat(tableFilters.dataCollectionFilter()).isInstanceOf(CachedTableFilter.class);
+    }
+
+    @Test
+    void testSetDataCollectionFiltersRetainsReplacement() {
+        RelationalTableFilters tableFilters = createTableFilters();
+        TableFilter replacementFilter = tableId -> false;
+
+        tableFilters.setDataCollectionFilters(replacementFilter);
+
+        
assertThat(tableFilters.dataCollectionFilter()).isSameAs(replacementFilter);
+    }
+
+    private static RelationalTableFilters createTableFilters() {
+        return new RelationalTableFilters(
+                Configuration.empty(), tableId -> true, TableId::toString);
+    }
+}
diff --git 
a/flink-cdc-connect/flink-cdc-source-connectors/flink-connector-mysql-cdc/src/main/java/org/apache/flink/cdc/connectors/mysql/source/config/MySqlSourceConfig.java
 
b/flink-cdc-connect/flink-cdc-source-connectors/flink-connector-mysql-cdc/src/main/java/org/apache/flink/cdc/connectors/mysql/source/config/MySqlSourceConfig.java
index cf456fcae..6b87dfef9 100644
--- 
a/flink-cdc-connect/flink-cdc-source-connectors/flink-connector-mysql-cdc/src/main/java/org/apache/flink/cdc/connectors/mysql/source/config/MySqlSourceConfig.java
+++ 
b/flink-cdc-connect/flink-cdc-source-connectors/flink-connector-mysql-cdc/src/main/java/org/apache/flink/cdc/connectors/mysql/source/config/MySqlSourceConfig.java
@@ -24,6 +24,7 @@ import org.apache.flink.table.catalog.ObjectPath;
 
 import io.debezium.config.Configuration;
 import io.debezium.connector.mysql.MySqlConnectorConfig;
+import io.debezium.relational.CachedTableFilter;
 import io.debezium.relational.RelationalTableFilters;
 import io.debezium.relational.TableId;
 import io.debezium.relational.Tables;
@@ -143,14 +144,16 @@ public class MySqlSourceConfig implements Serializable {
                 (excludeTableList == null
                         ? null
                         : new 
Selectors.SelectorsBuilder().includeTables(excludeTableList).build());
-        Tables.TableFilter tableFilter = 
dbzMySqlConfig.getTableFilters().dataCollectionFilter();
-        dbzMySqlConfig
-                .getTableFilters()
-                .setDataCollectionFilters(
-                        (TableId tableId) ->
-                                tableFilter.isIncluded(tableId)
-                                        && (excludeTableFilter == null
-                                                || 
!excludeTableFilter.isMatch(tableId)));
+        if (excludeTableFilter != null) {
+            Tables.TableFilter tableFilter =
+                    dbzMySqlConfig.getTableFilters().dataCollectionFilter();
+            dbzMySqlConfig
+                    .getTableFilters()
+                    .setDataCollectionFilters(
+                            CachedTableFilter.from(tableFilter)
+                                    .withAdditionalFilter(
+                                            tableId -> 
!excludeTableFilter.isMatch(tableId)));
+        }
         this.jdbcProperties = jdbcProperties;
         this.chunkKeyColumns = chunkKeyColumns;
         this.skipSnapshotBackfill = skipSnapshotBackfill;
diff --git 
a/flink-cdc-connect/flink-cdc-source-connectors/flink-connector-mysql-cdc/src/test/java/org/apache/flink/cdc/connectors/mysql/source/config/MySqlSourceConfigTest.java
 
b/flink-cdc-connect/flink-cdc-source-connectors/flink-connector-mysql-cdc/src/test/java/org/apache/flink/cdc/connectors/mysql/source/config/MySqlSourceConfigTest.java
new file mode 100644
index 000000000..5a88eada4
--- /dev/null
+++ 
b/flink-cdc-connect/flink-cdc-source-connectors/flink-connector-mysql-cdc/src/test/java/org/apache/flink/cdc/connectors/mysql/source/config/MySqlSourceConfigTest.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.flink.cdc.connectors.mysql.source.config;
+
+import io.debezium.relational.CachedTableFilter;
+import io.debezium.relational.TableId;
+import org.junit.jupiter.api.Test;
+
+import java.util.function.Predicate;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/** Tests for {@link MySqlSourceConfig}. */
+class MySqlSourceConfigTest {
+
+    @Test
+    void testTableFilterWithExcludeTableList() {
+        MySqlSourceConfig config =
+                new MySqlSourceConfigFactory()
+                        .hostname("localhost")
+                        .username("user")
+                        .password("password")
+                        .databaseList("test_db")
+                        .tableList("test_db\\.orders_.*")
+                        .excludeTableList("test_db.orders_skip")
+                        .createConfig(0);
+
+        Predicate<TableId> tableFilter = config.getTableFilter();
+        TableId includedTable = new TableId("test_db", null, "orders_1");
+        TableId excludedTable = new TableId("test_db", null, "orders_skip");
+        TableId unmatchedTable = new TableId("test_db", null, "customers");
+
+        assertThat(config.getTableFilters().dataCollectionFilter())
+                .isInstanceOf(CachedTableFilter.class);
+        assertThat(tableFilter.test(includedTable)).isTrue();
+        assertThat(tableFilter.test(excludedTable)).isFalse();
+        assertThat(tableFilter.test(unmatchedTable)).isFalse();
+    }
+}

Reply via email to