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 9f23c03569 [FLINK-40331][cdc-connect][fluss] Support forward shuffle
in fluss sink. (#4498)
9f23c03569 is described below
commit 9f23c035694d305ccf267cf2866f39e8e9713fa5
Author: Hongshun Wang <[email protected]>
AuthorDate: Tue Sep 8 10:15:54 2026 +0800
[FLINK-40331][cdc-connect][fluss] Support forward shuffle in fluss sink.
(#4498)
---
.../docs/connectors/pipeline-connectors/fluss.md | 7 +++
.../docs/connectors/pipeline-connectors/fluss.md | 7 +++
.../cdc/common/function/DefaultHashContext.java | 52 ++++++++++++++++++++
.../flink/cdc/common/function/HashFunction.java | 23 +++++++++
.../common/sink/ForwardHashFunctionProvider.java | 54 ++++++++++++++++++++
.../flink/cdc/common/function/HashContextTest.java | 55 +++++++++++++++++++++
.../sink/ForwardHashFunctionProviderTest.java | 57 ++++++++++++++++++++++
.../fluss/factory/FlussDataSinkFactory.java | 9 +++-
.../cdc/connectors/fluss/sink/FlussDataSink.java | 16 +++++-
.../fluss/sink/FlussDataSinkOptions.java | 17 +++++++
.../fluss/factory/FlussDataSinkFactoryTest.java | 21 ++++++++
.../BatchRegularPrePartitionOperator.java | 8 ++-
.../DistributedPrePartitionOperator.java | 20 +++++---
.../partitioning/RegularPrePartitionOperator.java | 8 ++-
.../partitioning/PrePartitionOperatorTest.java | 38 ++++++++++++++-
15 files changed, 380 insertions(+), 12 deletions(-)
diff --git a/docs/content.zh/docs/connectors/pipeline-connectors/fluss.md
b/docs/content.zh/docs/connectors/pipeline-connectors/fluss.md
index 1ac06c19c1..7fda6154bb 100644
--- a/docs/content.zh/docs/connectors/pipeline-connectors/fluss.md
+++ b/docs/content.zh/docs/connectors/pipeline-connectors/fluss.md
@@ -99,6 +99,13 @@ Pipeline Connector Options
<td>String</td>
<td>用于建立与 Fluss 集群初始连接的主机/端口对列表。 </td>
</tr>
+ <tr>
+ <td>sink.partitioning.strategy</td>
+ <td>optional</td>
+ <td style="word-wrap: break-word;">DEFAULT</td>
+ <td>String</td>
+ <td>DataChangeEvent 路由使用的分区策略。可选值为 <code>DEFAULT</code> 和
<code>FORWARD</code>。<code>DEFAULT</code> 对主键表按主键进行哈希分区,对日志表采用随机分发,将事件分散到各下游
subtask 以实现负载均衡。<code>FORWARD</code> 将数据事件发送到与上游 subtask 索引相同的下游 subtask,用于
Fluss 到 Fluss 的数据同步。上游数据分布必须与 Fluss 表的分桶策略保持一致,否则可能出现数据正确性问题。</td>
+ </tr>
<tr>
<td>bucket.key</td>
<td>optional</td>
diff --git a/docs/content/docs/connectors/pipeline-connectors/fluss.md
b/docs/content/docs/connectors/pipeline-connectors/fluss.md
index a2d7c53249..7bf8813aa3 100644
--- a/docs/content/docs/connectors/pipeline-connectors/fluss.md
+++ b/docs/content/docs/connectors/pipeline-connectors/fluss.md
@@ -100,6 +100,13 @@ Pipeline Connector Options
<td>String</td>
<td>The bootstrap servers for the Fluss sink connection. </td>
</tr>
+ <tr>
+ <td>sink.partitioning.strategy</td>
+ <td>optional</td>
+ <td style="word-wrap: break-word;">DEFAULT</td>
+ <td>String</td>
+ <td>The partitioning strategy for DataChangeEvent routing. Available
values are <code>DEFAULT</code> and <code>FORWARD</code>. <code>DEFAULT</code>
hashes primary key tables by primary keys and randomly distributes log table
events across downstream subtasks for balanced load. <code>FORWARD</code>
routes data events to downstream subtasks with the same indices as upstream and
is intended for Fluss-to-Fluss data synchronization. The upstream data
distribution must match the Fluss ta [...]
+ </tr>
<tr>
<td>bucket.key</td>
<td>optional</td>
diff --git
a/flink-cdc-common/src/main/java/org/apache/flink/cdc/common/function/DefaultHashContext.java
b/flink-cdc-common/src/main/java/org/apache/flink/cdc/common/function/DefaultHashContext.java
new file mode 100644
index 0000000000..8afb520a23
--- /dev/null
+++
b/flink-cdc-common/src/main/java/org/apache/flink/cdc/common/function/DefaultHashContext.java
@@ -0,0 +1,52 @@
+/*
+ * 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.common.function;
+
+import org.apache.flink.cdc.common.annotation.Internal;
+import org.apache.flink.cdc.common.utils.Preconditions;
+
+/** Default implementation of {@link HashFunction.HashContext}. */
+@Internal
+public final class DefaultHashContext implements HashFunction.HashContext {
+
+ private final int sourceSubtaskIndex;
+ private final int downstreamParallelism;
+
+ public DefaultHashContext(int sourceSubtaskIndex, int
downstreamParallelism) {
+ Preconditions.checkArgument(
+ sourceSubtaskIndex >= 0,
+ "sourceSubtaskIndex must be greater than or equal to 0, but
was %s.",
+ sourceSubtaskIndex);
+ Preconditions.checkArgument(
+ downstreamParallelism > 0,
+ "downstreamParallelism must be greater than 0, but was %s.",
+ downstreamParallelism);
+ this.sourceSubtaskIndex = sourceSubtaskIndex;
+ this.downstreamParallelism = downstreamParallelism;
+ }
+
+ @Override
+ public int getSourceSubtaskIndex() {
+ return sourceSubtaskIndex;
+ }
+
+ @Override
+ public int getDownstreamParallelism() {
+ return downstreamParallelism;
+ }
+}
diff --git
a/flink-cdc-common/src/main/java/org/apache/flink/cdc/common/function/HashFunction.java
b/flink-cdc-common/src/main/java/org/apache/flink/cdc/common/function/HashFunction.java
index 323a3de1af..a487ef684d 100644
---
a/flink-cdc-common/src/main/java/org/apache/flink/cdc/common/function/HashFunction.java
+++
b/flink-cdc-common/src/main/java/org/apache/flink/cdc/common/function/HashFunction.java
@@ -27,5 +27,28 @@ import org.apache.flink.cdc.common.annotation.Internal;
@Internal
public interface HashFunction<T> {
+ /** Runtime context for calculating an event hash code. */
+ @Internal
+ interface HashContext {
+ int getSourceSubtaskIndex();
+
+ int getDownstreamParallelism();
+ }
+
+ /** Creates a hash context. */
+ static HashContext createContext(int sourceSubtaskIndex, int
downstreamParallelism) {
+ return new DefaultHashContext(sourceSubtaskIndex,
downstreamParallelism);
+ }
+
int hashcode(T event);
+
+ /**
+ * Calculates the hash code with the runtime hash context.
+ *
+ * <p>Implementations that do not depend on the context can continue
implementing {@link
+ * #hashcode(Object)} only.
+ */
+ default int hashcode(HashContext context, T event) {
+ return hashcode(event);
+ }
}
diff --git
a/flink-cdc-common/src/main/java/org/apache/flink/cdc/common/sink/ForwardHashFunctionProvider.java
b/flink-cdc-common/src/main/java/org/apache/flink/cdc/common/sink/ForwardHashFunctionProvider.java
new file mode 100644
index 0000000000..52b70e3edb
--- /dev/null
+++
b/flink-cdc-common/src/main/java/org/apache/flink/cdc/common/sink/ForwardHashFunctionProvider.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.common.sink;
+
+import org.apache.flink.cdc.common.annotation.Internal;
+import org.apache.flink.cdc.common.event.DataChangeEvent;
+import org.apache.flink.cdc.common.event.TableId;
+import org.apache.flink.cdc.common.function.HashFunction;
+import org.apache.flink.cdc.common.function.HashFunction.HashContext;
+import org.apache.flink.cdc.common.function.HashFunctionProvider;
+import org.apache.flink.cdc.common.schema.Schema;
+
+import javax.annotation.Nullable;
+
+/** A {@link HashFunctionProvider} that preserves the upstream subtask
distribution. */
+@Internal
+public class ForwardHashFunctionProvider implements
HashFunctionProvider<DataChangeEvent> {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public HashFunction<DataChangeEvent> getHashFunction(@Nullable TableId
tableId, Schema schema) {
+ return new ForwardHashFunction();
+ }
+
+ private static class ForwardHashFunction implements
HashFunction<DataChangeEvent> {
+
+ @Override
+ public int hashcode(DataChangeEvent event) {
+ throw new UnsupportedOperationException(
+ "Forward hash function requires a HashContext.");
+ }
+
+ @Override
+ public int hashcode(HashContext context, DataChangeEvent event) {
+ return context.getSourceSubtaskIndex();
+ }
+ }
+}
diff --git
a/flink-cdc-common/src/test/java/org/apache/flink/cdc/common/function/HashContextTest.java
b/flink-cdc-common/src/test/java/org/apache/flink/cdc/common/function/HashContextTest.java
new file mode 100644
index 0000000000..9c30358f52
--- /dev/null
+++
b/flink-cdc-common/src/test/java/org/apache/flink/cdc/common/function/HashContextTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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.common.function;
+
+import org.apache.flink.cdc.common.function.HashFunction.HashContext;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+/** Unit tests for {@link HashContext}. */
+class HashContextTest {
+
+ @Test
+ void testProperties() {
+ HashContext context = HashFunction.createContext(3, 5);
+
+ assertThat(context).isInstanceOf(DefaultHashContext.class);
+ assertThat(context.getSourceSubtaskIndex()).isEqualTo(3);
+ assertThat(context.getDownstreamParallelism()).isEqualTo(5);
+ }
+
+ @Test
+ void testRejectsNegativeSourceSubtaskIndex() {
+ assertThatThrownBy(() -> HashFunction.createContext(-1, 1))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessageContaining("sourceSubtaskIndex");
+ }
+
+ @ParameterizedTest
+ @ValueSource(ints = {0, -1})
+ void testRejectsNonPositiveDownstreamParallelism(int
downstreamParallelism) {
+ assertThatThrownBy(() -> HashFunction.createContext(0,
downstreamParallelism))
+ .isInstanceOf(IllegalArgumentException.class)
+ .hasMessageContaining("downstreamParallelism");
+ }
+}
diff --git
a/flink-cdc-common/src/test/java/org/apache/flink/cdc/common/sink/ForwardHashFunctionProviderTest.java
b/flink-cdc-common/src/test/java/org/apache/flink/cdc/common/sink/ForwardHashFunctionProviderTest.java
new file mode 100644
index 0000000000..6750ba8fdf
--- /dev/null
+++
b/flink-cdc-common/src/test/java/org/apache/flink/cdc/common/sink/ForwardHashFunctionProviderTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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.common.sink;
+
+import org.apache.flink.cdc.common.data.GenericRecordData;
+import org.apache.flink.cdc.common.event.DataChangeEvent;
+import org.apache.flink.cdc.common.event.TableId;
+import org.apache.flink.cdc.common.function.HashFunction;
+import org.apache.flink.cdc.common.schema.Schema;
+
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+/** Unit tests for {@link ForwardHashFunctionProvider}. */
+class ForwardHashFunctionProviderTest {
+
+ @Test
+ void testForwardingBySourceIndex() {
+ DataChangeEvent event =
+ DataChangeEvent.insertEvent(TableId.tableId("customers"),
GenericRecordData.of(1));
+ HashFunction<DataChangeEvent> hashFunction =
+ new ForwardHashFunctionProvider()
+ .getHashFunction(event.tableId(),
Schema.newBuilder().build());
+
+ assertThat(hashFunction.hashcode(HashFunction.createContext(3, 5),
event)).isEqualTo(3);
+ }
+
+ @Test
+ void testHashingWithoutContextFails() {
+ DataChangeEvent event =
+ DataChangeEvent.insertEvent(TableId.tableId("customers"),
GenericRecordData.of(1));
+ HashFunction<DataChangeEvent> hashFunction =
+ new ForwardHashFunctionProvider()
+ .getHashFunction(event.tableId(),
Schema.newBuilder().build());
+
+ assertThatThrownBy(() -> hashFunction.hashcode(event))
+ .isInstanceOf(UnsupportedOperationException.class)
+ .hasMessageContaining("HashContext");
+ }
+}
diff --git
a/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-fluss/src/main/java/org/apache/flink/cdc/connectors/fluss/factory/FlussDataSinkFactory.java
b/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-fluss/src/main/java/org/apache/flink/cdc/connectors/fluss/factory/FlussDataSinkFactory.java
index ca3a6a98ef..455bbdc171 100644
---
a/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-fluss/src/main/java/org/apache/flink/cdc/connectors/fluss/factory/FlussDataSinkFactory.java
+++
b/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-fluss/src/main/java/org/apache/flink/cdc/connectors/fluss/factory/FlussDataSinkFactory.java
@@ -36,6 +36,7 @@ import static
org.apache.flink.cdc.connectors.fluss.sink.FlussDataSinkOptions.BO
import static
org.apache.flink.cdc.connectors.fluss.sink.FlussDataSinkOptions.BUCKET_KEY;
import static
org.apache.flink.cdc.connectors.fluss.sink.FlussDataSinkOptions.BUCKET_NUMBER;
import static
org.apache.flink.cdc.connectors.fluss.sink.FlussDataSinkOptions.CLIENT_PROPERTIES_PREFIX;
+import static
org.apache.flink.cdc.connectors.fluss.sink.FlussDataSinkOptions.SINK_PARTITIONING_STRATEGY;
import static
org.apache.flink.cdc.connectors.fluss.sink.FlussDataSinkOptions.TABLE_PROPERTIES_PREFIX;
import static
org.apache.flink.cdc.connectors.fluss.utils.FlussConfigUtils.parseBucketKeys;
import static
org.apache.flink.cdc.connectors.fluss.utils.FlussConfigUtils.parseBucketNumber;
@@ -57,7 +58,12 @@ public class FlussDataSinkFactory implements DataSinkFactory
{
parseBucketKeys(factoryConfiguration.get(BUCKET_KEY));
Map<String, Integer> bucketNumMap =
parseBucketNumber(factoryConfiguration.get(BUCKET_NUMBER));
- return new FlussDataSink(flussClientConfig, tableProperties,
bucketKeysMap, bucketNumMap);
+ return new FlussDataSink(
+ flussClientConfig,
+ tableProperties,
+ bucketKeysMap,
+ bucketNumMap,
+ factoryConfiguration.get(SINK_PARTITIONING_STRATEGY));
}
@Override
@@ -77,6 +83,7 @@ public class FlussDataSinkFactory implements DataSinkFactory {
Set<ConfigOption<?>> options = new HashSet<>();
options.add(BUCKET_KEY);
options.add(BUCKET_NUMBER);
+ options.add(SINK_PARTITIONING_STRATEGY);
return options;
}
diff --git
a/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-fluss/src/main/java/org/apache/flink/cdc/connectors/fluss/sink/FlussDataSink.java
b/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-fluss/src/main/java/org/apache/flink/cdc/connectors/fluss/sink/FlussDataSink.java
index 4491e50321..8af81355c8 100644
---
a/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-fluss/src/main/java/org/apache/flink/cdc/connectors/fluss/sink/FlussDataSink.java
+++
b/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-fluss/src/main/java/org/apache/flink/cdc/connectors/fluss/sink/FlussDataSink.java
@@ -22,7 +22,9 @@ import
org.apache.flink.cdc.common.function.HashFunctionProvider;
import org.apache.flink.cdc.common.sink.DataSink;
import org.apache.flink.cdc.common.sink.EventSinkProvider;
import org.apache.flink.cdc.common.sink.FlinkSinkProvider;
+import org.apache.flink.cdc.common.sink.ForwardHashFunctionProvider;
import org.apache.flink.cdc.common.sink.MetadataApplier;
+import
org.apache.flink.cdc.connectors.fluss.sink.FlussDataSinkOptions.SinkPartitioningStrategy;
import org.apache.flink.cdc.connectors.fluss.sink.v2.FlussSink;
import org.apache.fluss.config.Configuration;
@@ -37,16 +39,19 @@ public class FlussDataSink implements DataSink {
private final Map<String, String> tableProperties;
private final Map<String, List<String>> bucketKeysMap;
private final Map<String, Integer> bucketNumMap;
+ private final SinkPartitioningStrategy sinkPartitioningStrategy;
public FlussDataSink(
Configuration flussClientConfig,
Map<String, String> tableProperties,
Map<String, List<String>> bucketKeysMap,
- Map<String, Integer> bucketNumMap) {
+ Map<String, Integer> bucketNumMap,
+ SinkPartitioningStrategy sinkPartitioningStrategy) {
this.flussClientConfig = flussClientConfig;
this.tableProperties = tableProperties;
this.bucketKeysMap = bucketKeysMap;
this.bucketNumMap = bucketNumMap;
+ this.sinkPartitioningStrategy = sinkPartitioningStrategy;
}
@Override
@@ -63,6 +68,13 @@ public class FlussDataSink implements DataSink {
@Override
public HashFunctionProvider<DataChangeEvent>
getDataChangeEventHashFunctionProvider() {
- return new FlussHashFunctionProvider();
+ switch (sinkPartitioningStrategy) {
+ case DEFAULT:
+ return new FlussHashFunctionProvider();
+ case FORWARD:
+ return new ForwardHashFunctionProvider();
+ }
+ throw new IllegalArgumentException(
+ "Unsupported sink partitioning strategy: " +
sinkPartitioningStrategy);
}
}
diff --git
a/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-fluss/src/main/java/org/apache/flink/cdc/connectors/fluss/sink/FlussDataSinkOptions.java
b/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-fluss/src/main/java/org/apache/flink/cdc/connectors/fluss/sink/FlussDataSinkOptions.java
index f14fc1b67f..51287fca46 100644
---
a/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-fluss/src/main/java/org/apache/flink/cdc/connectors/fluss/sink/FlussDataSinkOptions.java
+++
b/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-fluss/src/main/java/org/apache/flink/cdc/connectors/fluss/sink/FlussDataSinkOptions.java
@@ -33,6 +33,17 @@ public class FlussDataSinkOptions {
.noDefaultValue()
.withDescription("The bootstrap servers for the Fluss sink
connection.");
+ public static final ConfigOption<SinkPartitioningStrategy>
SINK_PARTITIONING_STRATEGY =
+ ConfigOptions.key("sink.partitioning.strategy")
+ .enumType(SinkPartitioningStrategy.class)
+ .defaultValue(SinkPartitioningStrategy.DEFAULT)
+ .withDescription(
+ "Partitioning strategy for DataChangeEvent
routing. "
+ + "DEFAULT hashes primary key tables by
primary keys and randomly distributes log table events across downstream
subtasks for balanced load. "
+ + "FORWARD routes data events to
downstream subtasks with the same indices as upstream. "
+ + "FORWARD is intended for Fluss-to-Fluss
data synchronization. "
+ + "The upstream data distribution must
match the Fluss table bucket distribution; otherwise, data correctness issues
may occur.");
+
public static final ConfigOption<String> BUCKET_KEY =
ConfigOptions.key("bucket.key")
.stringType()
@@ -54,4 +65,10 @@ public class FlussDataSinkOptions {
"The number of buckets of each Fluss table."
+ "Tables are separated by ';'. "
+ "Format:
database1.table1:4;database1.table2:8.");
+
+ /** The shuffle strategy for fluss sink. */
+ public enum SinkPartitioningStrategy {
+ DEFAULT,
+ FORWARD
+ }
}
diff --git
a/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-fluss/src/test/java/org/apache/flink/cdc/connectors/fluss/factory/FlussDataSinkFactoryTest.java
b/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-fluss/src/test/java/org/apache/flink/cdc/connectors/fluss/factory/FlussDataSinkFactoryTest.java
index 49ec01d300..7b287d122c 100644
---
a/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-fluss/src/test/java/org/apache/flink/cdc/connectors/fluss/factory/FlussDataSinkFactoryTest.java
+++
b/flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-fluss/src/test/java/org/apache/flink/cdc/connectors/fluss/factory/FlussDataSinkFactoryTest.java
@@ -21,9 +21,12 @@ import
org.apache.flink.cdc.common.configuration.Configuration;
import org.apache.flink.cdc.common.factories.DataSinkFactory;
import org.apache.flink.cdc.common.factories.FactoryHelper;
import org.apache.flink.cdc.common.sink.DataSink;
+import org.apache.flink.cdc.common.sink.ForwardHashFunctionProvider;
import org.apache.flink.cdc.composer.utils.FactoryDiscoveryUtils;
import org.apache.flink.cdc.connectors.fluss.sink.FlussDataSink;
import org.apache.flink.cdc.connectors.fluss.sink.FlussDataSinkOptions;
+import
org.apache.flink.cdc.connectors.fluss.sink.FlussDataSinkOptions.SinkPartitioningStrategy;
+import org.apache.flink.cdc.connectors.fluss.sink.FlussHashFunctionProvider;
import org.apache.flink.table.api.ValidationException;
import org.apache.flink.shaded.guava31.com.google.common.collect.ImmutableMap;
@@ -47,6 +50,24 @@ public class FlussDataSinkFactoryTest {
new FactoryHelper.DefaultContext(
conf, conf,
Thread.currentThread().getContextClassLoader()));
Assertions.assertThat(dataSink).isInstanceOf(FlussDataSink.class);
+
Assertions.assertThat(dataSink.getDataChangeEventHashFunctionProvider())
+ .isInstanceOf(FlussHashFunctionProvider.class);
+ }
+
+ @Test
+ void testCreateDataSinkWithForwardPartitioningStrategy() {
+ DataSinkFactory sinkFactory =
+ FactoryDiscoveryUtils.getFactoryByIdentifier("fluss",
DataSinkFactory.class);
+ Configuration conf = createValidConfiguration();
+ conf.set(FlussDataSinkOptions.SINK_PARTITIONING_STRATEGY,
SinkPartitioningStrategy.FORWARD);
+
+ DataSink dataSink =
+ sinkFactory.createDataSink(
+ new FactoryHelper.DefaultContext(
+ conf, conf,
Thread.currentThread().getContextClassLoader()));
+
+
Assertions.assertThat(dataSink.getDataChangeEventHashFunctionProvider())
+ .isInstanceOf(ForwardHashFunctionProvider.class);
}
@Test
diff --git
a/flink-cdc-runtime/src/main/java/org/apache/flink/cdc/runtime/partitioning/BatchRegularPrePartitionOperator.java
b/flink-cdc-runtime/src/main/java/org/apache/flink/cdc/runtime/partitioning/BatchRegularPrePartitionOperator.java
index 1a2a697986..f5314fc676 100644
---
a/flink-cdc-runtime/src/main/java/org/apache/flink/cdc/runtime/partitioning/BatchRegularPrePartitionOperator.java
+++
b/flink-cdc-runtime/src/main/java/org/apache/flink/cdc/runtime/partitioning/BatchRegularPrePartitionOperator.java
@@ -23,6 +23,7 @@ import org.apache.flink.cdc.common.event.DataChangeEvent;
import org.apache.flink.cdc.common.event.Event;
import org.apache.flink.cdc.common.event.TableId;
import org.apache.flink.cdc.common.function.HashFunction;
+import org.apache.flink.cdc.common.function.HashFunction.HashContext;
import org.apache.flink.cdc.common.function.HashFunctionProvider;
import org.apache.flink.cdc.common.schema.Schema;
import org.apache.flink.cdc.runtime.operators.AbstractStreamOperatorAdapter;
@@ -56,6 +57,7 @@ public class BatchRegularPrePartitionOperator
private transient Map<TableId, HashFunction<DataChangeEvent>>
cachedHashFunctions;
private transient volatile Map<TableId, Schema> originalSchemaMap;
+ private transient HashContext hashContext;
public BatchRegularPrePartitionOperator(
int downstreamParallelism, HashFunctionProvider<DataChangeEvent>
hashFunctionProvider) {
@@ -69,6 +71,10 @@ public class BatchRegularPrePartitionOperator
super.open();
cachedHashFunctions = new HashMap<>();
originalSchemaMap = new HashMap<>();
+ hashContext =
+ HashFunction.createContext(
+
getRuntimeContext().getTaskInfo().getIndexOfThisSubtask(),
+ downstreamParallelism);
}
@Override
@@ -95,7 +101,7 @@ public class BatchRegularPrePartitionOperator
dataChangeEvent,
cachedHashFunctions
.get(dataChangeEvent.tableId())
- .hashcode(dataChangeEvent)
+ .hashcode(hashContext,
dataChangeEvent)
% downstreamParallelism)));
}
diff --git
a/flink-cdc-runtime/src/main/java/org/apache/flink/cdc/runtime/partitioning/DistributedPrePartitionOperator.java
b/flink-cdc-runtime/src/main/java/org/apache/flink/cdc/runtime/partitioning/DistributedPrePartitionOperator.java
index 9231abf1a8..7a984088b1 100644
---
a/flink-cdc-runtime/src/main/java/org/apache/flink/cdc/runtime/partitioning/DistributedPrePartitionOperator.java
+++
b/flink-cdc-runtime/src/main/java/org/apache/flink/cdc/runtime/partitioning/DistributedPrePartitionOperator.java
@@ -23,6 +23,7 @@ import org.apache.flink.cdc.common.event.Event;
import org.apache.flink.cdc.common.event.SchemaChangeEvent;
import org.apache.flink.cdc.common.event.TableId;
import org.apache.flink.cdc.common.function.HashFunction;
+import org.apache.flink.cdc.common.function.HashFunction.HashContext;
import org.apache.flink.cdc.common.function.HashFunctionProvider;
import org.apache.flink.cdc.common.schema.Schema;
import org.apache.flink.cdc.common.utils.SchemaUtils;
@@ -53,7 +54,7 @@ public class DistributedPrePartitionOperator
private transient Map<TableId, Schema> schemaMap;
private transient Map<TableId, HashFunction<DataChangeEvent>>
hashFunctionMap;
- private transient int subTaskId;
+ private transient HashContext hashContext;
public DistributedPrePartitionOperator(
int downstreamParallelism, HashFunctionProvider<DataChangeEvent>
hashFunctionProvider) {
@@ -65,7 +66,10 @@ public class DistributedPrePartitionOperator
@Override
public void open() throws Exception {
super.open();
- subTaskId =
RuntimeContextAdapter.getIndexOfThisSubtask(getRuntimeContext());
+ hashContext =
+ HashFunction.createContext(
+
RuntimeContextAdapter.getIndexOfThisSubtask(getRuntimeContext()),
+ downstreamParallelism);
schemaMap = new HashMap<>();
hashFunctionMap = new HashMap<>();
}
@@ -93,7 +97,9 @@ public class DistributedPrePartitionOperator
partitionBy((DataChangeEvent) event);
} else {
throw new IllegalStateException(
- subTaskId + "> PrePartition operator received an
unexpected event: " + event);
+ hashContext.getSourceSubtaskIndex()
+ + "> PrePartition operator received an unexpected
event: "
+ + event);
}
}
@@ -102,10 +108,10 @@ public class DistributedPrePartitionOperator
new StreamRecord<>(
PartitioningEvent.ofDistributed(
dataChangeEvent,
- subTaskId,
+ hashContext.getSourceSubtaskIndex(),
hashFunctionMap
.get(dataChangeEvent.tableId())
- .hashcode(dataChangeEvent)
+ .hashcode(hashContext,
dataChangeEvent)
% downstreamParallelism)));
}
@@ -115,7 +121,9 @@ public class DistributedPrePartitionOperator
// JVM
Event copiedEvent = EventSerializer.INSTANCE.copy(toBroadcast);
output.collect(
- new
StreamRecord<>(PartitioningEvent.ofDistributed(copiedEvent, subTaskId, i)));
+ new StreamRecord<>(
+ PartitioningEvent.ofDistributed(
+ copiedEvent,
hashContext.getSourceSubtaskIndex(), i)));
}
}
diff --git
a/flink-cdc-runtime/src/main/java/org/apache/flink/cdc/runtime/partitioning/RegularPrePartitionOperator.java
b/flink-cdc-runtime/src/main/java/org/apache/flink/cdc/runtime/partitioning/RegularPrePartitionOperator.java
index a5d81bfb19..6b72396b4d 100644
---
a/flink-cdc-runtime/src/main/java/org/apache/flink/cdc/runtime/partitioning/RegularPrePartitionOperator.java
+++
b/flink-cdc-runtime/src/main/java/org/apache/flink/cdc/runtime/partitioning/RegularPrePartitionOperator.java
@@ -24,6 +24,7 @@ import org.apache.flink.cdc.common.event.FlushEvent;
import org.apache.flink.cdc.common.event.SchemaChangeEvent;
import org.apache.flink.cdc.common.event.TableId;
import org.apache.flink.cdc.common.function.HashFunction;
+import org.apache.flink.cdc.common.function.HashFunction.HashContext;
import org.apache.flink.cdc.common.function.HashFunctionProvider;
import org.apache.flink.cdc.common.schema.Schema;
import org.apache.flink.cdc.runtime.operators.AbstractStreamOperatorAdapter;
@@ -62,6 +63,7 @@ public class RegularPrePartitionOperator extends
AbstractStreamOperatorAdapter<P
private transient SchemaEvolutionClient schemaEvolutionClient;
private transient LoadingCache<TableId, HashFunction<DataChangeEvent>>
cachedHashFunctions;
+ private transient HashContext hashContext;
public RegularPrePartitionOperator(
OperatorID schemaOperatorId,
@@ -80,6 +82,10 @@ public class RegularPrePartitionOperator extends
AbstractStreamOperatorAdapter<P
getContainingTask().getEnvironment().getOperatorCoordinatorEventGateway();
schemaEvolutionClient = new SchemaEvolutionClient(toCoordinator,
schemaOperatorId);
cachedHashFunctions = createCache();
+ hashContext =
+ HashFunction.createContext(
+
getRuntimeContext().getTaskInfo().getIndexOfThisSubtask(),
+ downstreamParallelism);
}
@Override
@@ -107,7 +113,7 @@ public class RegularPrePartitionOperator extends
AbstractStreamOperatorAdapter<P
dataChangeEvent,
cachedHashFunctions
.get(dataChangeEvent.tableId())
- .hashcode(dataChangeEvent)
+ .hashcode(hashContext,
dataChangeEvent)
% downstreamParallelism)));
}
diff --git
a/flink-cdc-runtime/src/test/java/org/apache/flink/cdc/runtime/partitioning/PrePartitionOperatorTest.java
b/flink-cdc-runtime/src/test/java/org/apache/flink/cdc/runtime/partitioning/PrePartitionOperatorTest.java
index 4273f97cdc..692e3835b1 100644
---
a/flink-cdc-runtime/src/test/java/org/apache/flink/cdc/runtime/partitioning/PrePartitionOperatorTest.java
+++
b/flink-cdc-runtime/src/test/java/org/apache/flink/cdc/runtime/partitioning/PrePartitionOperatorTest.java
@@ -25,6 +25,7 @@ import
org.apache.flink.cdc.common.event.SchemaChangeEventType;
import org.apache.flink.cdc.common.event.TableId;
import org.apache.flink.cdc.common.schema.Schema;
import
org.apache.flink.cdc.common.sink.DefaultDataChangeEventHashFunctionProvider;
+import org.apache.flink.cdc.common.sink.ForwardHashFunctionProvider;
import org.apache.flink.cdc.common.sink.TableIdHashFunctionProvider;
import org.apache.flink.cdc.common.types.DataTypes;
import org.apache.flink.cdc.common.types.RowType;
@@ -39,7 +40,7 @@ import java.util.Collections;
import static org.assertj.core.api.Assertions.assertThat;
-/** Unit test for {@link RegularPrePartitionOperator}. */
+/** Unit tests for pre-partition operators. */
class PrePartitionOperatorTest {
private static final TableId CUSTOMERS =
TableId.tableId("my_company", "my_branch", "customers");
@@ -140,6 +141,31 @@ class PrePartitionOperatorTest {
}
}
+ @Test
+ void testForwardingDataChangeEvent() throws Exception {
+ try (RegularEventOperatorTestHarness<RegularPrePartitionOperator,
PartitioningEvent>
+ testHarness = createDataForwardTestHarness()) {
+ testHarness.open();
+ testHarness.registerTableSchema(CUSTOMERS, CUSTOMERS_SCHEMA);
+
+ DataChangeEvent dataChangeEvent = createDataChangeEvent();
+ testHarness.getOperator().processElement(new
StreamRecord<>(dataChangeEvent));
+
+ assertThat(testHarness.getOutputRecords())
+ .containsExactly(
+ new
StreamRecord<>(PartitioningEvent.ofRegular(dataChangeEvent, 0)));
+ }
+ }
+
+ private DataChangeEvent createDataChangeEvent() {
+ BinaryRecordDataGenerator recordDataGenerator =
+ new BinaryRecordDataGenerator(((RowType)
CUSTOMERS_SCHEMA.toRowDataType()));
+ return DataChangeEvent.insertEvent(
+ CUSTOMERS,
+ recordDataGenerator.generate(
+ new Object[] {1, new BinaryStringData("Alice"),
12345678L}));
+ }
+
private int getPartitioningTarget(Schema schema, DataChangeEvent
dataChangeEvent) {
return new DefaultDataChangeEventHashFunctionProvider()
.getHashFunction(null, schema)
@@ -228,4 +254,14 @@ class PrePartitionOperatorTest {
new DefaultDataChangeEventHashFunctionProvider());
return RegularEventOperatorTestHarness.with(operator,
DOWNSTREAM_PARALLELISM);
}
+
+ private RegularEventOperatorTestHarness<RegularPrePartitionOperator,
PartitioningEvent>
+ createDataForwardTestHarness() {
+ RegularPrePartitionOperator operator =
+ new RegularPrePartitionOperator(
+ TestingSchemaRegistryGateway.SCHEMA_OPERATOR_ID,
+ DOWNSTREAM_PARALLELISM,
+ new ForwardHashFunctionProvider());
+ return RegularEventOperatorTestHarness.with(operator,
DOWNSTREAM_PARALLELISM);
+ }
}