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

luoyuxia pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fluss.git


The following commit(s) were added to refs/heads/main by this push:
     new d90a9563e [lake/hudi] Add Flink union read test base for Hudi (#3522)
d90a9563e is described below

commit d90a9563e3e67b9b0734d79025e5090474c53229
Author: fhan <[email protected]>
AuthorDate: Thu Jun 25 17:18:42 2026 +0800

    [lake/hudi] Add Flink union read test base for Hudi (#3522)
---
 .../lake/hudi/flink/FlinkUnionReadTestBase.java    | 97 ++++++++++++++++++++++
 .../hudi/testutils/FlinkHudiTieringTestBase.java   | 34 ++++++++
 2 files changed, 131 insertions(+)

diff --git 
a/fluss-lake/fluss-lake-hudi/src/test/java/org/apache/fluss/lake/hudi/flink/FlinkUnionReadTestBase.java
 
b/fluss-lake/fluss-lake-hudi/src/test/java/org/apache/fluss/lake/hudi/flink/FlinkUnionReadTestBase.java
new file mode 100644
index 000000000..243c65290
--- /dev/null
+++ 
b/fluss-lake/fluss-lake-hudi/src/test/java/org/apache/fluss/lake/hudi/flink/FlinkUnionReadTestBase.java
@@ -0,0 +1,97 @@
+/*
+ * 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.fluss.lake.hudi.flink;
+
+import org.apache.fluss.config.ConfigOptions;
+import org.apache.fluss.lake.hudi.testutils.FlinkHudiTieringTestBase;
+import org.apache.fluss.server.testutils.FlussClusterExtension;
+
+import org.apache.flink.configuration.Configuration;
+import org.apache.flink.table.api.EnvironmentSettings;
+import org.apache.flink.table.api.bridge.java.StreamTableEnvironment;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import javax.annotation.Nullable;
+
+import static org.apache.fluss.flink.FlinkConnectorOptions.BOOTSTRAP_SERVERS;
+
+/** Base class for Hudi union read tests. */
+public class FlinkUnionReadTestBase extends FlinkHudiTieringTestBase {
+
+    @RegisterExtension
+    public static final FlussClusterExtension FLUSS_CLUSTER_EXTENSION =
+            FlussClusterExtension.builder()
+                    .setClusterConf(initConfig())
+                    .setNumOfTabletServers(3)
+                    .build();
+
+    protected static final int DEFAULT_BUCKET_NUM = 1;
+    StreamTableEnvironment batchTEnv;
+    StreamTableEnvironment streamTEnv;
+
+    @BeforeAll
+    protected static void beforeAll() {
+        
FlinkHudiTieringTestBase.beforeAll(FLUSS_CLUSTER_EXTENSION.getClientConfig());
+    }
+
+    @BeforeEach
+    public void beforeEach() {
+        super.beforeEach();
+        buildBatchTEnv();
+        buildStreamTEnv();
+    }
+
+    @Override
+    protected FlussClusterExtension getFlussClusterExtension() {
+        return FLUSS_CLUSTER_EXTENSION;
+    }
+
+    protected StreamTableEnvironment buildStreamTEnv(@Nullable String 
savepointPath) {
+        Configuration conf = new Configuration();
+        if (savepointPath != null) {
+            conf.setString("execution.savepoint.path", savepointPath);
+            execEnv.configure(conf);
+        }
+        String bootstrapServers = String.join(",", 
clientConf.get(ConfigOptions.BOOTSTRAP_SERVERS));
+        streamTEnv = StreamTableEnvironment.create(execEnv, 
EnvironmentSettings.inStreamingMode());
+        streamTEnv.executeSql(
+                String.format(
+                        "create catalog %s with ('type' = 'fluss', '%s' = 
'%s')",
+                        CATALOG_NAME, BOOTSTRAP_SERVERS.key(), 
bootstrapServers));
+        streamTEnv.executeSql("use catalog " + CATALOG_NAME);
+        streamTEnv.executeSql("use " + DEFAULT_DB);
+        return streamTEnv;
+    }
+
+    private void buildStreamTEnv() {
+        buildStreamTEnv(null);
+    }
+
+    public void buildBatchTEnv() {
+        String bootstrapServers = String.join(",", 
clientConf.get(ConfigOptions.BOOTSTRAP_SERVERS));
+        batchTEnv = StreamTableEnvironment.create(execEnv, 
EnvironmentSettings.inBatchMode());
+        batchTEnv.executeSql(
+                String.format(
+                        "create catalog %s with ('type' = 'fluss', '%s' = 
'%s')",
+                        CATALOG_NAME, BOOTSTRAP_SERVERS.key(), 
bootstrapServers));
+        batchTEnv.executeSql("use catalog " + CATALOG_NAME);
+        batchTEnv.executeSql("use " + DEFAULT_DB);
+    }
+}
diff --git 
a/fluss-lake/fluss-lake-hudi/src/test/java/org/apache/fluss/lake/hudi/testutils/FlinkHudiTieringTestBase.java
 
b/fluss-lake/fluss-lake-hudi/src/test/java/org/apache/fluss/lake/hudi/testutils/FlinkHudiTieringTestBase.java
index f678c3766..40de1ed42 100644
--- 
a/fluss-lake/fluss-lake-hudi/src/test/java/org/apache/fluss/lake/hudi/testutils/FlinkHudiTieringTestBase.java
+++ 
b/fluss-lake/fluss-lake-hudi/src/test/java/org/apache/fluss/lake/hudi/testutils/FlinkHudiTieringTestBase.java
@@ -72,10 +72,12 @@ import java.time.Duration;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
+import java.util.Set;
 import java.util.stream.Collectors;
 import java.util.stream.IntStream;
 
@@ -321,6 +323,14 @@ public abstract class FlinkHudiTieringTestBase {
         return getFlussClusterExtension().waitAndGetLeaderReplica(tableBucket);
     }
 
+    protected void assertReplicaStatus(Map<TableBucket, Long> 
expectedLogEndOffset) {
+        for (Map.Entry<TableBucket, Long> expectedLogEndOffsetEntry :
+                expectedLogEndOffset.entrySet()) {
+            assertReplicaStatus(
+                    expectedLogEndOffsetEntry.getKey(), 
expectedLogEndOffsetEntry.getValue());
+        }
+    }
+
     protected void assertReplicaStatus(TableBucket tb, long 
expectedLogEndOffset) {
         retry(
                 Duration.ofMinutes(1),
@@ -332,6 +342,30 @@ public abstract class FlinkHudiTieringTestBase {
                 });
     }
 
+    protected void waitUntilBucketSynced(
+            TablePath tablePath, long tableId, int bucketCount, boolean 
isPartitioned) {
+        Set<TableBucket> tableBuckets = new HashSet<>();
+        if (isPartitioned) {
+            Map<Long, String> partitionById = waitUntilPartitions(tablePath);
+            for (Long partitionId : partitionById.keySet()) {
+                for (int i = 0; i < bucketCount; i++) {
+                    tableBuckets.add(new TableBucket(tableId, partitionId, i));
+                }
+            }
+        } else {
+            for (int i = 0; i < bucketCount; i++) {
+                tableBuckets.add(new TableBucket(tableId, i));
+            }
+        }
+        waitUntilBucketsSynced(tableBuckets);
+    }
+
+    protected void waitUntilBucketsSynced(Set<TableBucket> tableBuckets) {
+        for (TableBucket tableBucket : tableBuckets) {
+            waitUntilBucketSynced(tableBucket);
+        }
+    }
+
     protected void waitUntilBucketSynced(TableBucket tb) {
         waitUntil(
                 () -> 
getLeaderReplica(tb).getLogTablet().getLakeTableSnapshotId() >= 0,

Reply via email to