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

yuxia 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 6fb4aec49 [lance] introduce lance connector and implement lake catalog 
(#1242)
6fb4aec49 is described below

commit 6fb4aec49c70e57f6dcd919e37945b988489d595
Author: xx789 <[email protected]>
AuthorDate: Thu Jul 31 16:32:38 2025 +0800

    [lance] introduce lance connector and implement lake catalog (#1242)
---
 .github/workflows/stage.sh                         |   3 +-
 .../alibaba/fluss/bucketing/BucketingFunction.java |   2 +
 .../com/alibaba/fluss/metadata/DataLakeFormat.java |   1 +
 .../com/alibaba/fluss/row/encode/KeyEncoder.java   |   3 +
 fluss-dist/pom.xml                                 |   7 +
 fluss-dist/src/main/assemblies/plugins.xml         |   8 +-
 fluss-lake/fluss-lake-lance/pom.xml                | 225 +++++++++++++++++++++
 .../com/alibaba/fluss/lake/lance/LanceConfig.java  | 168 +++++++++++++++
 .../alibaba/fluss/lake/lance/LanceLakeCatalog.java |  84 ++++++++
 .../alibaba/fluss/lake/lance/LanceLakeStorage.java |  43 ++++
 .../fluss/lake/lance/LanceLakeStoragePlugin.java   |  23 ++-
 .../fluss/lake/lance/tiering/LanceCommittable.java |  22 +-
 .../fluss/lake/lance/tiering/LanceWriteResult.java |  25 ++-
 .../fluss/lake/lance/utils/LanceArrowUtils.java    | 190 +++++++++++++++++
 .../lake/lance/utils/LanceDatasetAdapter.java      |  49 +++++
 .../src/main/resources/META-INF/NOTICE             |  37 ++++
 .../licenses/LICENSE-EDL-1.0.eclipse-collections   |  13 ++
 .../LICENSE-EDL-1.0.eclipse-collections-api        |  13 ++
 .../licenses/LICENSE-EPL-1.0.eclipse-collections   |  70 +++++++
 .../LICENSE-EPL-1.0.eclipse-collections-api        |  70 +++++++
 .../resources/META-INF/licenses/LICENSE.slf4j-api  |  21 ++
 .../resources/META-INF/licenses/LICENSE.zstd-jni   |  26 +++
 ...libaba.fluss.lake.lakestorage.LakeStoragePlugin |  17 ++
 .../lake/lance/LakeEnabledTableCreateITCase.java   | 204 +++++++++++++++++++
 .../src/test/resources/log4j2-test.properties      |  27 +++
 .../org.junit.jupiter.api.extension.Extension      |  19 ++
 fluss-lake/pom.xml                                 |   1 +
 fluss-test-coverage/pom.xml                        |   2 +
 28 files changed, 1338 insertions(+), 35 deletions(-)

diff --git a/.github/workflows/stage.sh b/.github/workflows/stage.sh
index 8d10d9b15..e96d34ddf 100755
--- a/.github/workflows/stage.sh
+++ b/.github/workflows/stage.sh
@@ -28,7 +28,8 @@ fluss-flink/fluss-flink-1.19,\
 fluss-flink/fluss-flink-1.18,\
 fluss-lake,\
 fluss-lake/fluss-lake-paimon,\
-fluss-lake/fluss-lake-iceberg
+fluss-lake/fluss-lake-iceberg,\
+fluss-lake/fluss-lake-lance
 "
 
 function get_test_modules_for_stage() {
diff --git 
a/fluss-common/src/main/java/com/alibaba/fluss/bucketing/BucketingFunction.java 
b/fluss-common/src/main/java/com/alibaba/fluss/bucketing/BucketingFunction.java
index b08ed770d..8b06ae3c1 100644
--- 
a/fluss-common/src/main/java/com/alibaba/fluss/bucketing/BucketingFunction.java
+++ 
b/fluss-common/src/main/java/com/alibaba/fluss/bucketing/BucketingFunction.java
@@ -39,6 +39,8 @@ public interface BucketingFunction {
             return new FlussBucketingFunction();
         } else if (lakeFormat == DataLakeFormat.PAIMON) {
             return new PaimonBucketingFunction();
+        } else if (lakeFormat == DataLakeFormat.LANCE) {
+            return new FlussBucketingFunction();
         } else {
             throw new UnsupportedOperationException("Unsupported lake format: 
" + lakeFormat);
         }
diff --git 
a/fluss-common/src/main/java/com/alibaba/fluss/metadata/DataLakeFormat.java 
b/fluss-common/src/main/java/com/alibaba/fluss/metadata/DataLakeFormat.java
index b014ee814..af4af09f4 100644
--- a/fluss-common/src/main/java/com/alibaba/fluss/metadata/DataLakeFormat.java
+++ b/fluss-common/src/main/java/com/alibaba/fluss/metadata/DataLakeFormat.java
@@ -20,6 +20,7 @@ package com.alibaba.fluss.metadata;
 /** An enum for datalake format. */
 public enum DataLakeFormat {
     PAIMON("paimon"),
+    LANCE("lance"),
     ICEBERG("iceberg");
 
     private final String value;
diff --git 
a/fluss-common/src/main/java/com/alibaba/fluss/row/encode/KeyEncoder.java 
b/fluss-common/src/main/java/com/alibaba/fluss/row/encode/KeyEncoder.java
index da3306455..b9574aa8c 100644
--- a/fluss-common/src/main/java/com/alibaba/fluss/row/encode/KeyEncoder.java
+++ b/fluss-common/src/main/java/com/alibaba/fluss/row/encode/KeyEncoder.java
@@ -47,6 +47,9 @@ public interface KeyEncoder {
             return CompactedKeyEncoder.createKeyEncoder(rowType, keyFields);
         } else if (lakeFormat == DataLakeFormat.PAIMON) {
             return new PaimonKeyEncoder(rowType, keyFields);
+        } else if (lakeFormat == DataLakeFormat.LANCE) {
+            // use default compacted key encoder
+            return CompactedKeyEncoder.createKeyEncoder(rowType, keyFields);
         } else if (lakeFormat == DataLakeFormat.ICEBERG) {
             return new IcebergKeyEncoder(rowType, keyFields);
         } else {
diff --git a/fluss-dist/pom.xml b/fluss-dist/pom.xml
index 43dd80f00..21dfa7c89 100644
--- a/fluss-dist/pom.xml
+++ b/fluss-dist/pom.xml
@@ -104,6 +104,13 @@
             <scope>provided</scope>
         </dependency>
 
+        <dependency>
+            <groupId>com.alibaba.fluss</groupId>
+            <artifactId>fluss-lake-lance</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+      
         <dependency>
             <groupId>org.apache.flink</groupId>
             <artifactId>flink-shaded-hadoop-2-uber</artifactId>
diff --git a/fluss-dist/src/main/assemblies/plugins.xml 
b/fluss-dist/src/main/assemblies/plugins.xml
index 3bffaf44c..9b5ffc3bf 100644
--- a/fluss-dist/src/main/assemblies/plugins.xml
+++ b/fluss-dist/src/main/assemblies/plugins.xml
@@ -101,13 +101,17 @@
             <destName>fluss-lake-paimon-${project.version}.jar</destName>
             <fileMode>0644</fileMode>
         </file>
-
         <file>
             
<source>../fluss-lake/fluss-lake-iceberg/target/fluss-lake-iceberg-${project.version}.jar</source>
             <outputDirectory>plugins/iceberg/</outputDirectory>
             <destName>fluss-lake-iceberg-${project.version}.jar</destName>
             <fileMode>0644</fileMode>
         </file>
-    </files>
+        <file>
+            
<source>../fluss-lake/fluss-lake-lance/target/fluss-lake-lance-${project.version}.jar</source>
+            <outputDirectory>plugins/lance/</outputDirectory>
+            <destName>fluss-lake-lance-${project.version}.jar</destName>
+        </file>
+  </files>
 
 </assembly>
\ No newline at end of file
diff --git a/fluss-lake/fluss-lake-lance/pom.xml 
b/fluss-lake/fluss-lake-lance/pom.xml
new file mode 100644
index 000000000..65715230d
--- /dev/null
+++ b/fluss-lake/fluss-lake-lance/pom.xml
@@ -0,0 +1,225 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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.
+-->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0";
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd";>
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>com.alibaba.fluss</groupId>
+        <artifactId>fluss-lake</artifactId>
+        <version>0.8-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>fluss-lake-lance</artifactId>
+    <name>Fluss : Lake : Lance</name>
+
+    <packaging>jar</packaging>
+
+    <properties>
+        <lance.version>0.26.1</lance.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>com.alibaba.fluss</groupId>
+            <artifactId>fluss-common</artifactId>
+            <version>${project.version}</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>com.lancedb</groupId>
+            <artifactId>lance-core</artifactId>
+            <version>${lance.version}</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba.fluss</groupId>
+            <artifactId>fluss-client</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba.fluss</groupId>
+            <artifactId>fluss-test-utils</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.curator</groupId>
+            <artifactId>curator-test</artifactId>
+            <version>${curator.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba.fluss</groupId>
+            <artifactId>fluss-server</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.hadoop</groupId>
+            <artifactId>hadoop-hdfs-client</artifactId>
+            <version>${fluss.hadoop.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.hadoop</groupId>
+            <artifactId>hadoop-common</artifactId>
+            <scope>provided</scope>
+            <exclusions>
+                <exclusion>
+                    <artifactId>avro</artifactId>
+                    <groupId>org.apache.avro</groupId>
+                </exclusion>
+                <exclusion>
+                    <artifactId>log4j</artifactId>
+                    <groupId>log4j</groupId>
+                </exclusion>
+                <exclusion>
+                    <artifactId>slf4j-log4j12</artifactId>
+                    <groupId>org.slf4j</groupId>
+                </exclusion>
+                <exclusion>
+                    <groupId>ch.qos.reload4j</groupId>
+                    <artifactId>reload4j</artifactId>
+                </exclusion>
+                <exclusion>
+                    <groupId>org.slf4j</groupId>
+                    <artifactId>slf4j-reload4j</artifactId>
+                </exclusion>
+                <exclusion>
+                    <artifactId>jdk.tools</artifactId>
+                    <groupId>jdk.tools</groupId>
+                </exclusion>
+                <exclusion>
+                    <artifactId>protobuf-java</artifactId>
+                    <groupId>com.google.protobuf</groupId>
+                </exclusion>
+                <exclusion>
+                    <artifactId>commons-io</artifactId>
+                    <groupId>commons-io</groupId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba.fluss</groupId>
+            <artifactId>fluss-server</artifactId>
+            <version>${project.version}</version>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+
+        <!-- Flink test dependency -->
+        <dependency>
+            <groupId>com.alibaba.fluss</groupId>
+            <artifactId>fluss-flink-${flink.major.version}</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba.fluss</groupId>
+            <artifactId>fluss-common</artifactId>
+            <version>${project.version}</version>
+            <type>test-jar</type>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba.fluss</groupId>
+            <artifactId>fluss-flink-common</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.flink</groupId>
+            <artifactId>flink-core</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.flink</groupId>
+            <artifactId>flink-table-common</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.flink</groupId>
+            <artifactId>flink-table-runtime</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.flink</groupId>
+            <artifactId>flink-connector-base</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.flink</groupId>
+            <artifactId>flink-connector-files</artifactId>
+            <scope>test</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.flink</groupId>
+            <artifactId>flink-table-test-utils</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-shade-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>shade-fluss</id>
+                        <phase>package</phase>
+                        <goals>
+                            <goal>shade</goal>
+                        </goals>
+                        <configuration>
+                            <artifactSet>
+                                <includes>
+                                    <include>*:*</include>
+                                </includes>
+                            </artifactSet>
+                            <filters>
+                                <filter>
+                                    <artifact>*</artifact>
+                                    <excludes>
+                                        <exclude>LICENSE*</exclude>
+                                    </excludes>
+                                </filter>
+                            </filters>
+                        </configuration>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git 
a/fluss-lake/fluss-lake-lance/src/main/java/com/alibaba/fluss/lake/lance/LanceConfig.java
 
b/fluss-lake/fluss-lake-lance/src/main/java/com/alibaba/fluss/lake/lance/LanceConfig.java
new file mode 100644
index 000000000..5d7218d89
--- /dev/null
+++ 
b/fluss-lake/fluss-lake-lance/src/main/java/com/alibaba/fluss/lake/lance/LanceConfig.java
@@ -0,0 +1,168 @@
+/*
+ * 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 com.alibaba.fluss.lake.lance;
+
+import com.lancedb.lance.ReadOptions;
+import com.lancedb.lance.WriteParams;
+
+import java.io.Serializable;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+
+/** Lance Configuration. */
+public class LanceConfig implements Serializable {
+    private static final long serialVersionUID = 1L;
+    public static final String LANCE_FILE_SUFFIX = ".lance";
+
+    private static final String block_size = "block_size";
+    private static final String version = "version";
+    private static final String index_cache_size = "index_cache_size";
+    private static final String metadata_cache_size = "metadata_cache_size";
+    private static final String max_row_per_file = "max_row_per_file";
+    private static final String max_rows_per_group = "max_rows_per_group";
+    private static final String max_bytes_per_file = "max_bytes_per_file";
+    private static final String ak = "access_key_id";
+    private static final String sk = "secret_access_key";
+    private static final String endpoint = "aws_endpoint";
+    private static final String region = "aws_region";
+    private static final String virtual_hosted_style = 
"virtual_hosted_style_request";
+    private static final String allow_http = "allow_http";
+    private static final String batch_size = "batch_size";
+    private static final String warehouse = "warehouse";
+
+    private final Map<String, String> options;
+    private final String databaseName;
+    private final String tableName;
+    private final String datasetUri;
+
+    public LanceConfig(
+            String databaseName,
+            String tableName,
+            String warehouse,
+            Map<String, String> properties) {
+        this.databaseName = databaseName;
+        this.tableName = tableName;
+        this.options = properties;
+
+        this.datasetUri = warehouse + "/" + databaseName + "/" + tableName + 
LANCE_FILE_SUFFIX;
+    }
+
+    public static LanceConfig from(
+            Map<String, String> properties, String databaseName, String 
tableName) {
+        if (!properties.containsKey(warehouse)) {
+            throw new IllegalArgumentException("Missing required option " + 
warehouse);
+        }
+        return new LanceConfig(databaseName, tableName, 
properties.get(warehouse), properties);
+    }
+
+    public static int getBatchSize(LanceConfig config) {
+        Map<String, String> options = config.getOptions();
+        if (options.containsKey(batch_size)) {
+            return Integer.parseInt(options.get(batch_size));
+        }
+        return 512;
+    }
+
+    public Map<String, String> getOptions() {
+        return options;
+    }
+
+    public String getDatabaseName() {
+        return databaseName;
+    }
+
+    public String getTableName() {
+        return tableName;
+    }
+
+    public String getDatasetUri() {
+        return datasetUri;
+    }
+
+    public static ReadOptions genReadOptionFromConfig(LanceConfig config) {
+        ReadOptions.Builder builder = new ReadOptions.Builder();
+        Map<String, String> maps = config.getOptions();
+        if (maps.containsKey(block_size)) {
+            builder.setBlockSize(Integer.parseInt(maps.get(block_size)));
+        }
+        if (maps.containsKey(version)) {
+            builder.setVersion(Integer.parseInt(maps.get(version)));
+        }
+        if (maps.containsKey(index_cache_size)) {
+            
builder.setIndexCacheSize(Integer.parseInt(maps.get(index_cache_size)));
+        }
+        if (maps.containsKey(metadata_cache_size)) {
+            
builder.setMetadataCacheSize(Integer.parseInt(maps.get(metadata_cache_size)));
+        }
+        builder.setStorageOptions(genStorageOptions(config));
+        return builder.build();
+    }
+
+    public static WriteParams genWriteParamsFromConfig(LanceConfig config) {
+        WriteParams.Builder builder = new WriteParams.Builder();
+        Map<String, String> maps = config.getOptions();
+        if (maps.containsKey(max_row_per_file)) {
+            
builder.withMaxRowsPerFile(Integer.parseInt(maps.get(max_row_per_file)));
+        }
+        if (maps.containsKey(max_rows_per_group)) {
+            
builder.withMaxRowsPerGroup(Integer.parseInt(maps.get(max_rows_per_group)));
+        }
+        if (maps.containsKey(max_bytes_per_file)) {
+            
builder.withMaxBytesPerFile(Long.parseLong(maps.get(max_bytes_per_file)));
+        }
+        builder.withStorageOptions(genStorageOptions(config));
+        return builder.build();
+    }
+
+    private static Map<String, String> genStorageOptions(LanceConfig config) {
+        Map<String, String> storageOptions = new HashMap<>();
+        Map<String, String> maps = config.getOptions();
+        if (maps.containsKey(ak) && maps.containsKey(sk) && 
maps.containsKey(endpoint)) {
+            storageOptions.put(ak, maps.get(ak));
+            storageOptions.put(sk, maps.get(sk));
+            storageOptions.put(endpoint, maps.get(endpoint));
+        }
+        if (maps.containsKey(region)) {
+            storageOptions.put(region, maps.get(region));
+        }
+        if (maps.containsKey(virtual_hosted_style)) {
+            storageOptions.put(virtual_hosted_style, 
maps.get(virtual_hosted_style));
+        }
+        if (maps.containsKey(allow_http)) {
+            storageOptions.put(allow_http, maps.get(allow_http));
+        }
+        return storageOptions;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        LanceConfig config = (LanceConfig) o;
+        return Objects.equals(databaseName, config.databaseName)
+                && Objects.equals(tableName, config.tableName)
+                && Objects.equals(options, config.options);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(databaseName, tableName, options);
+    }
+}
diff --git 
a/fluss-lake/fluss-lake-lance/src/main/java/com/alibaba/fluss/lake/lance/LanceLakeCatalog.java
 
b/fluss-lake/fluss-lake-lance/src/main/java/com/alibaba/fluss/lake/lance/LanceLakeCatalog.java
new file mode 100644
index 000000000..daaf26eb2
--- /dev/null
+++ 
b/fluss-lake/fluss-lake-lance/src/main/java/com/alibaba/fluss/lake/lance/LanceLakeCatalog.java
@@ -0,0 +1,84 @@
+/*
+ * 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 com.alibaba.fluss.lake.lance;
+
+import com.alibaba.fluss.config.Configuration;
+import com.alibaba.fluss.lake.lakestorage.LakeCatalog;
+import com.alibaba.fluss.lake.lance.utils.LanceArrowUtils;
+import com.alibaba.fluss.lake.lance.utils.LanceDatasetAdapter;
+import com.alibaba.fluss.metadata.TableDescriptor;
+import com.alibaba.fluss.metadata.TablePath;
+
+import com.lancedb.lance.WriteParams;
+import org.apache.arrow.vector.types.TimeUnit;
+import org.apache.arrow.vector.types.pojo.ArrowType;
+import org.apache.arrow.vector.types.pojo.Field;
+import org.apache.arrow.vector.types.pojo.Schema;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static com.alibaba.fluss.metadata.TableDescriptor.BUCKET_COLUMN_NAME;
+import static com.alibaba.fluss.metadata.TableDescriptor.OFFSET_COLUMN_NAME;
+import static com.alibaba.fluss.metadata.TableDescriptor.TIMESTAMP_COLUMN_NAME;
+
+/** A Lance implementation of {@link LakeCatalog}. */
+public class LanceLakeCatalog implements LakeCatalog {
+    private static final List<Field> SYSTEM_COLUMNS = new ArrayList<>();
+
+    static {
+        SYSTEM_COLUMNS.add(Field.nullable(BUCKET_COLUMN_NAME, new 
ArrowType.Int(32, true)));
+        SYSTEM_COLUMNS.add(Field.nullable(OFFSET_COLUMN_NAME, new 
ArrowType.Int(64, true)));
+        SYSTEM_COLUMNS.add(
+                Field.nullable(
+                        TIMESTAMP_COLUMN_NAME,
+                        new ArrowType.Timestamp(TimeUnit.MICROSECOND, null)));
+    }
+
+    private final Configuration options;
+
+    public LanceLakeCatalog(Configuration config) {
+        this.options = config;
+    }
+
+    @Override
+    public void createTable(TablePath tablePath, TableDescriptor 
tableDescriptor) {
+        LanceConfig config =
+                LanceConfig.from(
+                        options.toMap(), tablePath.getDatabaseName(), 
tablePath.getTableName());
+        WriteParams params = LanceConfig.genWriteParamsFromConfig(config);
+
+        List<Field> fields = new ArrayList<>();
+        // set schema
+        fields.addAll(
+                
LanceArrowUtils.toArrowSchema(tableDescriptor.getSchema().getRowType())
+                        .getFields());
+        // add system metadata columns to schema
+        fields.addAll(SYSTEM_COLUMNS);
+        try {
+            LanceDatasetAdapter.createDataset(config.getDatasetUri(), new 
Schema(fields), params);
+        } catch (RuntimeException e) {
+            throw new RuntimeException("Table " + tablePath + " creation 
failed", e);
+        }
+    }
+
+    @Override
+    public void close() throws Exception {
+        LakeCatalog.super.close();
+    }
+}
diff --git 
a/fluss-lake/fluss-lake-lance/src/main/java/com/alibaba/fluss/lake/lance/LanceLakeStorage.java
 
b/fluss-lake/fluss-lake-lance/src/main/java/com/alibaba/fluss/lake/lance/LanceLakeStorage.java
new file mode 100644
index 000000000..11e6fef28
--- /dev/null
+++ 
b/fluss-lake/fluss-lake-lance/src/main/java/com/alibaba/fluss/lake/lance/LanceLakeStorage.java
@@ -0,0 +1,43 @@
+/*
+ * 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 com.alibaba.fluss.lake.lance;
+
+import com.alibaba.fluss.config.Configuration;
+import com.alibaba.fluss.lake.lakestorage.LakeStorage;
+import com.alibaba.fluss.lake.lance.tiering.LanceCommittable;
+import com.alibaba.fluss.lake.lance.tiering.LanceWriteResult;
+import com.alibaba.fluss.lake.writer.LakeTieringFactory;
+
+/** Lance implementation of {@link LakeStorage}. */
+public class LanceLakeStorage implements LakeStorage {
+    private final Configuration config;
+
+    public LanceLakeStorage(Configuration configuration) {
+        this.config = configuration;
+    }
+
+    @Override
+    public LakeTieringFactory<LanceWriteResult, LanceCommittable> 
createLakeTieringFactory() {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public LanceLakeCatalog createLakeCatalog() {
+        return new LanceLakeCatalog(config);
+    }
+}
diff --git 
a/fluss-common/src/main/java/com/alibaba/fluss/metadata/DataLakeFormat.java 
b/fluss-lake/fluss-lake-lance/src/main/java/com/alibaba/fluss/lake/lance/LanceLakeStoragePlugin.java
similarity index 59%
copy from 
fluss-common/src/main/java/com/alibaba/fluss/metadata/DataLakeFormat.java
copy to 
fluss-lake/fluss-lake-lance/src/main/java/com/alibaba/fluss/lake/lance/LanceLakeStoragePlugin.java
index b014ee814..e5d65534d 100644
--- a/fluss-common/src/main/java/com/alibaba/fluss/metadata/DataLakeFormat.java
+++ 
b/fluss-lake/fluss-lake-lance/src/main/java/com/alibaba/fluss/lake/lance/LanceLakeStoragePlugin.java
@@ -15,21 +15,24 @@
  * limitations under the License.
  */
 
-package com.alibaba.fluss.metadata;
+package com.alibaba.fluss.lake.lance;
 
-/** An enum for datalake format. */
-public enum DataLakeFormat {
-    PAIMON("paimon"),
-    ICEBERG("iceberg");
+import com.alibaba.fluss.config.Configuration;
+import com.alibaba.fluss.lake.lakestorage.LakeStorage;
+import com.alibaba.fluss.lake.lakestorage.LakeStoragePlugin;
 
-    private final String value;
+/** Lance implementation of {@link LakeStoragePlugin}. */
+public class LanceLakeStoragePlugin implements LakeStoragePlugin {
 
-    DataLakeFormat(String value) {
-        this.value = value;
+    private static final String IDENTIFIER = "lance";
+
+    @Override
+    public String identifier() {
+        return IDENTIFIER;
     }
 
     @Override
-    public String toString() {
-        return value;
+    public LakeStorage createLakeStorage(Configuration configuration) {
+        return new LanceLakeStorage(configuration);
     }
 }
diff --git 
a/fluss-common/src/main/java/com/alibaba/fluss/metadata/DataLakeFormat.java 
b/fluss-lake/fluss-lake-lance/src/main/java/com/alibaba/fluss/lake/lance/tiering/LanceCommittable.java
similarity index 63%
copy from 
fluss-common/src/main/java/com/alibaba/fluss/metadata/DataLakeFormat.java
copy to 
fluss-lake/fluss-lake-lance/src/main/java/com/alibaba/fluss/lake/lance/tiering/LanceCommittable.java
index b014ee814..c54a444db 100644
--- a/fluss-common/src/main/java/com/alibaba/fluss/metadata/DataLakeFormat.java
+++ 
b/fluss-lake/fluss-lake-lance/src/main/java/com/alibaba/fluss/lake/lance/tiering/LanceCommittable.java
@@ -15,21 +15,21 @@
  * limitations under the License.
  */
 
-package com.alibaba.fluss.metadata;
+package com.alibaba.fluss.lake.lance.tiering;
 
-/** An enum for datalake format. */
-public enum DataLakeFormat {
-    PAIMON("paimon"),
-    ICEBERG("iceberg");
+import com.lancedb.lance.FragmentMetadata;
 
-    private final String value;
+import java.util.List;
 
-    DataLakeFormat(String value) {
-        this.value = value;
+/** The committable that derived from {@link LanceWriteResult} to commit to 
Lance. */
+public class LanceCommittable {
+    private final List<FragmentMetadata> committable;
+
+    public LanceCommittable(List<FragmentMetadata> committable) {
+        this.committable = committable;
     }
 
-    @Override
-    public String toString() {
-        return value;
+    public List<FragmentMetadata> committable() {
+        return committable;
     }
 }
diff --git 
a/fluss-common/src/main/java/com/alibaba/fluss/metadata/DataLakeFormat.java 
b/fluss-lake/fluss-lake-lance/src/main/java/com/alibaba/fluss/lake/lance/tiering/LanceWriteResult.java
similarity index 58%
copy from 
fluss-common/src/main/java/com/alibaba/fluss/metadata/DataLakeFormat.java
copy to 
fluss-lake/fluss-lake-lance/src/main/java/com/alibaba/fluss/lake/lance/tiering/LanceWriteResult.java
index b014ee814..b9528d153 100644
--- a/fluss-common/src/main/java/com/alibaba/fluss/metadata/DataLakeFormat.java
+++ 
b/fluss-lake/fluss-lake-lance/src/main/java/com/alibaba/fluss/lake/lance/tiering/LanceWriteResult.java
@@ -15,21 +15,24 @@
  * limitations under the License.
  */
 
-package com.alibaba.fluss.metadata;
+package com.alibaba.fluss.lake.lance.tiering;
 
-/** An enum for datalake format. */
-public enum DataLakeFormat {
-    PAIMON("paimon"),
-    ICEBERG("iceberg");
+import com.lancedb.lance.FragmentMetadata;
 
-    private final String value;
+import java.io.Serializable;
+import java.util.List;
 
-    DataLakeFormat(String value) {
-        this.value = value;
+/** The write result of Lance lake writer to pass to commiter to commit. */
+public class LanceWriteResult implements Serializable {
+    private static final long serialVersionUID = 1L;
+
+    private final List<FragmentMetadata> commitMessage;
+
+    public LanceWriteResult(List<FragmentMetadata> commitMessage) {
+        this.commitMessage = commitMessage;
     }
 
-    @Override
-    public String toString() {
-        return value;
+    public List<FragmentMetadata> commitMessage() {
+        return commitMessage;
     }
 }
diff --git 
a/fluss-lake/fluss-lake-lance/src/main/java/com/alibaba/fluss/lake/lance/utils/LanceArrowUtils.java
 
b/fluss-lake/fluss-lake-lance/src/main/java/com/alibaba/fluss/lake/lance/utils/LanceArrowUtils.java
new file mode 100644
index 000000000..cf92952c9
--- /dev/null
+++ 
b/fluss-lake/fluss-lake-lance/src/main/java/com/alibaba/fluss/lake/lance/utils/LanceArrowUtils.java
@@ -0,0 +1,190 @@
+/*
+ * 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 com.alibaba.fluss.lake.lance.utils;
+
+import com.alibaba.fluss.types.BigIntType;
+import com.alibaba.fluss.types.BinaryType;
+import com.alibaba.fluss.types.BooleanType;
+import com.alibaba.fluss.types.BytesType;
+import com.alibaba.fluss.types.CharType;
+import com.alibaba.fluss.types.DataType;
+import com.alibaba.fluss.types.DataTypeDefaultVisitor;
+import com.alibaba.fluss.types.DateType;
+import com.alibaba.fluss.types.DecimalType;
+import com.alibaba.fluss.types.DoubleType;
+import com.alibaba.fluss.types.FloatType;
+import com.alibaba.fluss.types.IntType;
+import com.alibaba.fluss.types.LocalZonedTimestampType;
+import com.alibaba.fluss.types.RowType;
+import com.alibaba.fluss.types.SmallIntType;
+import com.alibaba.fluss.types.StringType;
+import com.alibaba.fluss.types.TimeType;
+import com.alibaba.fluss.types.TimestampType;
+import com.alibaba.fluss.types.TinyIntType;
+
+import org.apache.arrow.vector.types.DateUnit;
+import org.apache.arrow.vector.types.FloatingPointPrecision;
+import org.apache.arrow.vector.types.TimeUnit;
+import org.apache.arrow.vector.types.pojo.ArrowType;
+import org.apache.arrow.vector.types.pojo.Field;
+import org.apache.arrow.vector.types.pojo.FieldType;
+import org.apache.arrow.vector.types.pojo.Schema;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/** Utilities for Arrow. */
+public class LanceArrowUtils {
+    /** Returns the Arrow schema of the specified type. */
+    public static Schema toArrowSchema(RowType rowType) {
+        List<Field> fields =
+                rowType.getFields().stream()
+                        .map(f -> toArrowField(f.getName(), f.getType()))
+                        .collect(Collectors.toList());
+        return new Schema(fields);
+    }
+
+    private static Field toArrowField(String fieldName, DataType logicalType) {
+        FieldType fieldType =
+                new FieldType(
+                        logicalType.isNullable(),
+                        
logicalType.accept(DataTypeToArrowTypeConverter.INSTANCE),
+                        null);
+        return new Field(fieldName, fieldType, null);
+    }
+
+    private static class DataTypeToArrowTypeConverter extends 
DataTypeDefaultVisitor<ArrowType> {
+
+        private static final DataTypeToArrowTypeConverter INSTANCE =
+                new DataTypeToArrowTypeConverter();
+
+        @Override
+        public ArrowType visit(TinyIntType tinyIntType) {
+            return new ArrowType.Int(8, true);
+        }
+
+        @Override
+        public ArrowType visit(SmallIntType smallIntType) {
+            return new ArrowType.Int(2 * 8, true);
+        }
+
+        @Override
+        public ArrowType visit(IntType intType) {
+            return new ArrowType.Int(4 * 8, true);
+        }
+
+        @Override
+        public ArrowType visit(BigIntType bigIntType) {
+            return new ArrowType.Int(8 * 8, true);
+        }
+
+        @Override
+        public ArrowType visit(BooleanType booleanType) {
+            return ArrowType.Bool.INSTANCE;
+        }
+
+        @Override
+        public ArrowType visit(FloatType floatType) {
+            return new ArrowType.FloatingPoint(FloatingPointPrecision.SINGLE);
+        }
+
+        @Override
+        public ArrowType visit(DoubleType doubleType) {
+            return new ArrowType.FloatingPoint(FloatingPointPrecision.DOUBLE);
+        }
+
+        @Override
+        public ArrowType visit(CharType varCharType) {
+            return ArrowType.Utf8.INSTANCE;
+        }
+
+        @Override
+        public ArrowType visit(StringType stringType) {
+            return ArrowType.Utf8.INSTANCE;
+        }
+
+        @Override
+        public ArrowType visit(BinaryType binaryType) {
+            return new ArrowType.FixedSizeBinary(binaryType.getLength());
+        }
+
+        @Override
+        public ArrowType visit(BytesType bytesType) {
+            return ArrowType.Binary.INSTANCE;
+        }
+
+        @Override
+        public ArrowType visit(DecimalType decimalType) {
+            return ArrowType.Decimal.createDecimal(
+                    decimalType.getPrecision(), decimalType.getScale(), null);
+        }
+
+        @Override
+        public ArrowType visit(DateType dateType) {
+            return new ArrowType.Date(DateUnit.DAY);
+        }
+
+        @Override
+        public ArrowType visit(TimeType timeType) {
+            if (timeType.getPrecision() == 0) {
+                return new ArrowType.Time(TimeUnit.SECOND, 32);
+            } else if (timeType.getPrecision() >= 1 && timeType.getPrecision() 
<= 3) {
+                return new ArrowType.Time(TimeUnit.MILLISECOND, 32);
+            } else if (timeType.getPrecision() >= 4 && timeType.getPrecision() 
<= 6) {
+                return new ArrowType.Time(TimeUnit.MICROSECOND, 64);
+            } else {
+                return new ArrowType.Time(TimeUnit.NANOSECOND, 64);
+            }
+        }
+
+        @Override
+        public ArrowType visit(LocalZonedTimestampType 
localZonedTimestampType) {
+            if (localZonedTimestampType.getPrecision() == 0) {
+                return new ArrowType.Timestamp(TimeUnit.SECOND, null);
+            } else if (localZonedTimestampType.getPrecision() >= 1
+                    && localZonedTimestampType.getPrecision() <= 3) {
+                return new ArrowType.Timestamp(TimeUnit.MILLISECOND, null);
+            } else if (localZonedTimestampType.getPrecision() >= 4
+                    && localZonedTimestampType.getPrecision() <= 6) {
+                return new ArrowType.Timestamp(TimeUnit.MICROSECOND, null);
+            } else {
+                return new ArrowType.Timestamp(TimeUnit.NANOSECOND, null);
+            }
+        }
+
+        @Override
+        public ArrowType visit(TimestampType timestampType) {
+            if (timestampType.getPrecision() == 0) {
+                return new ArrowType.Timestamp(TimeUnit.SECOND, null);
+            } else if (timestampType.getPrecision() >= 1 && 
timestampType.getPrecision() <= 3) {
+                return new ArrowType.Timestamp(TimeUnit.MILLISECOND, null);
+            } else if (timestampType.getPrecision() >= 4 && 
timestampType.getPrecision() <= 6) {
+                return new ArrowType.Timestamp(TimeUnit.MICROSECOND, null);
+            } else {
+                return new ArrowType.Timestamp(TimeUnit.NANOSECOND, null);
+            }
+        }
+
+        @Override
+        protected ArrowType defaultMethod(DataType dataType) {
+            throw new UnsupportedOperationException(
+                    String.format(
+                            "Unsupported data type %s currently.", 
dataType.asSummaryString()));
+        }
+    }
+}
diff --git 
a/fluss-lake/fluss-lake-lance/src/main/java/com/alibaba/fluss/lake/lance/utils/LanceDatasetAdapter.java
 
b/fluss-lake/fluss-lake-lance/src/main/java/com/alibaba/fluss/lake/lance/utils/LanceDatasetAdapter.java
new file mode 100644
index 000000000..ed50abcc9
--- /dev/null
+++ 
b/fluss-lake/fluss-lake-lance/src/main/java/com/alibaba/fluss/lake/lance/utils/LanceDatasetAdapter.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 com.alibaba.fluss.lake.lance.utils;
+
+import com.alibaba.fluss.lake.lance.LanceConfig;
+
+import com.lancedb.lance.Dataset;
+import com.lancedb.lance.ReadOptions;
+import com.lancedb.lance.WriteParams;
+import org.apache.arrow.memory.BufferAllocator;
+import org.apache.arrow.memory.RootAllocator;
+import org.apache.arrow.vector.types.pojo.Schema;
+
+import java.util.Optional;
+
+/** Lance dataset API adapter. */
+public class LanceDatasetAdapter {
+    private static final BufferAllocator allocator = new RootAllocator();
+
+    public static void createDataset(String datasetUri, Schema schema, 
WriteParams params) {
+        Dataset.create(allocator, datasetUri, schema, params).close();
+    }
+
+    public static Optional<Schema> getSchema(LanceConfig config) {
+        String uri = config.getDatasetUri();
+        ReadOptions options = LanceConfig.genReadOptionFromConfig(config);
+        try (Dataset dataset = Dataset.open(allocator, uri, options)) {
+            return Optional.of(dataset.getSchema());
+        } catch (IllegalArgumentException e) {
+            // dataset not found
+            return Optional.empty();
+        }
+    }
+}
diff --git a/fluss-lake/fluss-lake-lance/src/main/resources/META-INF/NOTICE 
b/fluss-lake/fluss-lake-lance/src/main/resources/META-INF/NOTICE
new file mode 100644
index 000000000..786813d32
--- /dev/null
+++ b/fluss-lake/fluss-lake-lance/src/main/resources/META-INF/NOTICE
@@ -0,0 +1,37 @@
+fluss-lake-lance
+Copyright 2025 The Apache Software Foundation
+
+This project includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+This project bundles the following dependencies under the Apache Software 
License 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt)
+
+- com.fasterxml.jackson.core:jackson-annotations:2.15.3
+- com.fasterxml.jackson.core:jackson-core:2.15.3
+- com.fasterxml.jackson.core:jackson-databind:2.15.3
+- com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.15.3
+- com.google.code.findbugs:jsr305:1.3.9
+- org.apache.commons:commons-lang3:3.18.0
+- io.netty:netty-buffer:4.1.104.Final
+- io.netty:netty-common:4.1.104.Final
+- org.apache.arrow:arrow-c-data:15.0.0
+- org.apache.arrow:arrow-dataset:15.0.0
+- org.apache.arrow:arrow-format:15.0.0
+- org.apache.arrow:arrow-memory-core:15.0.0
+- org.apache.arrow:arrow-memory-netty:15.0.0
+- org.apache.arrow:arrow-vector:15.0.0
+- org.questdb:jar-jni:1.1.1
+- com.lancedb:lance-core:0.26.1
+- commons-codec:commons-codec:1.4
+- com.google.flatbuffers:flatbuffers-java:23.5.26
+
+This project bundles the following dependencies under the MIT 
(https://opensource.org/licenses/MIT)
+See bundled license files for details.
+
+- org.slf4j:slf4j-api:1.7.36
+
+This project bundles the following dependencies under both EDL 1.0 License 
(https://www.eclipse.org/org/documents/edl-v10.php) and the Eclipse Public 
License - v 1.0 (https://www.eclipse.org/org/documents/epl-1.0/EPL-1.0.txt).
+See bundled license files for details.
+
+- org.eclipse.collections:eclipse-collections-api:11.1.0
+- org.eclipse.collections:eclipse-collections:11.1.0
\ No newline at end of file
diff --git 
a/fluss-lake/fluss-lake-lance/src/main/resources/META-INF/licenses/LICENSE-EDL-1.0.eclipse-collections
 
b/fluss-lake/fluss-lake-lance/src/main/resources/META-INF/licenses/LICENSE-EDL-1.0.eclipse-collections
new file mode 100644
index 000000000..332519708
--- /dev/null
+++ 
b/fluss-lake/fluss-lake-lance/src/main/resources/META-INF/licenses/LICENSE-EDL-1.0.eclipse-collections
@@ -0,0 +1,13 @@
+Eclipse Distribution License - v 1.0
+
+Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors.
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without 
modification, are permitted provided that the following conditions are met:
+
+    Redistributions of source code must retain the above copyright notice, 
this list of conditions and the following disclaimer.
+    Redistributions in binary form must reproduce the above copyright notice, 
this list of conditions and the following disclaimer in the documentation 
and/or other materials provided with the distribution.
+    Neither the name of the Eclipse Foundation, Inc. nor the names of its 
contributors may be used to endorse or promote products derived from this 
software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
LOSS OF USE, DATA, OR PROFI [...]
\ No newline at end of file
diff --git 
a/fluss-lake/fluss-lake-lance/src/main/resources/META-INF/licenses/LICENSE-EDL-1.0.eclipse-collections-api
 
b/fluss-lake/fluss-lake-lance/src/main/resources/META-INF/licenses/LICENSE-EDL-1.0.eclipse-collections-api
new file mode 100644
index 000000000..332519708
--- /dev/null
+++ 
b/fluss-lake/fluss-lake-lance/src/main/resources/META-INF/licenses/LICENSE-EDL-1.0.eclipse-collections-api
@@ -0,0 +1,13 @@
+Eclipse Distribution License - v 1.0
+
+Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors.
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without 
modification, are permitted provided that the following conditions are met:
+
+    Redistributions of source code must retain the above copyright notice, 
this list of conditions and the following disclaimer.
+    Redistributions in binary form must reproduce the above copyright notice, 
this list of conditions and the following disclaimer in the documentation 
and/or other materials provided with the distribution.
+    Neither the name of the Eclipse Foundation, Inc. nor the names of its 
contributors may be used to endorse or promote products derived from this 
software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
LOSS OF USE, DATA, OR PROFI [...]
\ No newline at end of file
diff --git 
a/fluss-lake/fluss-lake-lance/src/main/resources/META-INF/licenses/LICENSE-EPL-1.0.eclipse-collections
 
b/fluss-lake/fluss-lake-lance/src/main/resources/META-INF/licenses/LICENSE-EPL-1.0.eclipse-collections
new file mode 100644
index 000000000..3d967aee7
--- /dev/null
+++ 
b/fluss-lake/fluss-lake-lance/src/main/resources/META-INF/licenses/LICENSE-EPL-1.0.eclipse-collections
@@ -0,0 +1,70 @@
+Eclipse Public License - v 1.0
+
+THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC 
LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM 
CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
+
+1. DEFINITIONS
+
+"Contribution" means:
+
+a) in the case of the initial Contributor, the initial code and documentation 
distributed under this Agreement, and
+b) in the case of each subsequent Contributor:
+i) changes to the Program, and
+ii) additions to the Program;
+where such changes and/or additions to the Program originate from and are 
distributed by that particular Contributor. A Contribution 'originates' from a 
Contributor if it was added to the Program by such Contributor itself or anyone 
acting on such Contributor's behalf. Contributions do not include additions to 
the Program which: (i) are separate modules of software distributed in 
conjunction with the Program under their own license agreement, and (ii) are 
not derivative works of the Program.
+"Contributor" means any person or entity that distributes the Program.
+
+"Licensed Patents" mean patent claims licensable by a Contributor which are 
necessarily infringed by the use or sale of its Contribution alone or when 
combined with the Program.
+
+"Program" means the Contributions distributed in accordance with this 
Agreement.
+
+"Recipient" means anyone who receives the Program under this Agreement, 
including all Contributors.
+
+2. GRANT OF RIGHTS
+
+a) Subject to the terms of this Agreement, each Contributor hereby grants 
Recipient a non-exclusive, worldwide, royalty-free copyright license to 
reproduce, prepare derivative works of, publicly display, publicly perform, 
distribute and sublicense the Contribution of such Contributor, if any, and 
such derivative works, in source code and object code form.
+b) Subject to the terms of this Agreement, each Contributor hereby grants 
Recipient a non-exclusive, worldwide, royalty-free patent license under 
Licensed Patents to make, use, sell, offer to sell, import and otherwise 
transfer the Contribution of such Contributor, if any, in source code and 
object code form. This patent license shall apply to the combination of the 
Contribution and the Program if, at the time the Contribution is added by the 
Contributor, such addition of the Contributio [...]
+c) Recipient understands that although each Contributor grants the licenses to 
its Contributions set forth herein, no assurances are provided by any 
Contributor that the Program does not infringe the patent or other intellectual 
property rights of any other entity. Each Contributor disclaims any liability 
to Recipient for claims brought by any other entity based on infringement of 
intellectual property rights or otherwise. As a condition to exercising the 
rights and licenses granted here [...]
+d) Each Contributor represents that to its knowledge it has sufficient 
copyright rights in its Contribution, if any, to grant the copyright license 
set forth in this Agreement.
+3. REQUIREMENTS
+
+A Contributor may choose to distribute the Program in object code form under 
its own license agreement, provided that:
+
+a) it complies with the terms and conditions of this Agreement; and
+b) its license agreement:
+i) effectively disclaims on behalf of all Contributors all warranties and 
conditions, express and implied, including warranties or conditions of title 
and non-infringement, and implied warranties or conditions of merchantability 
and fitness for a particular purpose;
+ii) effectively excludes on behalf of all Contributors all liability for 
damages, including direct, indirect, special, incidental and consequential 
damages, such as lost profits;
+iii) states that any provisions which differ from this Agreement are offered 
by that Contributor alone and not by any other party; and
+iv) states that source code for the Program is available from such 
Contributor, and informs licensees how to obtain it in a reasonable manner on 
or through a medium customarily used for software exchange.
+When the Program is made available in source code form:
+
+a) it must be made available under this Agreement; and
+b) a copy of this Agreement must be included with each copy of the Program.
+Contributors may not remove or alter any copyright notices contained within 
the Program.
+
+Each Contributor must identify itself as the originator of its Contribution, 
if any, in a manner that reasonably allows subsequent Recipients to identify 
the originator of the Contribution.
+
+4. COMMERCIAL DISTRIBUTION
+
+Commercial distributors of software may accept certain responsibilities with 
respect to end users, business partners and the like. While this license is 
intended to facilitate the commercial use of the Program, the Contributor who 
includes the Program in a commercial product offering should do so in a manner 
which does not create potential liability for other Contributors. Therefore, if 
a Contributor includes the Program in a commercial product offering, such 
Contributor ("Commercial Con [...]
+
+For example, a Contributor might include the Program in a commercial product 
offering, Product X. That Contributor is then a Commercial Contributor. If that 
Commercial Contributor then makes performance claims, or offers warranties 
related to Product X, those performance claims and warranties are such 
Commercial Contributor's responsibility alone. Under this section, the 
Commercial Contributor would have to defend claims against the other 
Contributors related to those performance claims  [...]
+
+5. NO WARRANTY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN 
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR 
IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, 
NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each 
Recipient is solely responsible for determining the appropriateness of using 
and distributing the Program and assumes all risks associated with its exercise 
of rights under this Ag [...]
+
+6. DISCLAIMER OF LIABILITY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY 
CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST 
PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY 
WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS 
GRANTED HEREUNDER, EVEN IF [...]
+
+7. GENERAL
+
+If any provision of this Agreement is invalid or unenforceable under 
applicable law, it shall not affect the validity or enforceability of the 
remainder of the terms of this Agreement, and without further action by the 
parties hereto, such provision shall be reformed to the minimum extent 
necessary to make such provision valid and enforceable.
+
+If Recipient institutes patent litigation against any entity (including a 
cross-claim or counterclaim in a lawsuit) alleging that the Program itself 
(excluding combinations of the Program with other software or hardware) 
infringes such Recipient's patent(s), then such Recipient's rights granted 
under Section 2(b) shall terminate as of the date such litigation is filed.
+
+All Recipient's rights under this Agreement shall terminate if it fails to 
comply with any of the material terms or conditions of this Agreement and does 
not cure such failure in a reasonable period of time after becoming aware of 
such noncompliance. If all Recipient's rights under this Agreement terminate, 
Recipient agrees to cease use and distribution of the Program as soon as 
reasonably practicable. However, Recipient's obligations under this Agreement 
and any licenses granted by Reci [...]
+
+Everyone is permitted to copy and distribute copies of this Agreement, but in 
order to avoid inconsistency the Agreement is copyrighted and may only be 
modified in the following manner. The Agreement Steward reserves the right to 
publish new versions (including revisions) of this Agreement from time to time. 
No one other than the Agreement Steward has the right to modify this Agreement. 
The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation 
may assign the respons [...]
+
+This Agreement is governed by the laws of the State of New York and the 
intellectual property laws of the United States of America. No party to this 
Agreement will bring a legal action under this Agreement more than one year 
after the cause of action arose. Each party waives its rights to a jury trial 
in any resulting litigation.
\ No newline at end of file
diff --git 
a/fluss-lake/fluss-lake-lance/src/main/resources/META-INF/licenses/LICENSE-EPL-1.0.eclipse-collections-api
 
b/fluss-lake/fluss-lake-lance/src/main/resources/META-INF/licenses/LICENSE-EPL-1.0.eclipse-collections-api
new file mode 100644
index 000000000..3d967aee7
--- /dev/null
+++ 
b/fluss-lake/fluss-lake-lance/src/main/resources/META-INF/licenses/LICENSE-EPL-1.0.eclipse-collections-api
@@ -0,0 +1,70 @@
+Eclipse Public License - v 1.0
+
+THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC 
LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM 
CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
+
+1. DEFINITIONS
+
+"Contribution" means:
+
+a) in the case of the initial Contributor, the initial code and documentation 
distributed under this Agreement, and
+b) in the case of each subsequent Contributor:
+i) changes to the Program, and
+ii) additions to the Program;
+where such changes and/or additions to the Program originate from and are 
distributed by that particular Contributor. A Contribution 'originates' from a 
Contributor if it was added to the Program by such Contributor itself or anyone 
acting on such Contributor's behalf. Contributions do not include additions to 
the Program which: (i) are separate modules of software distributed in 
conjunction with the Program under their own license agreement, and (ii) are 
not derivative works of the Program.
+"Contributor" means any person or entity that distributes the Program.
+
+"Licensed Patents" mean patent claims licensable by a Contributor which are 
necessarily infringed by the use or sale of its Contribution alone or when 
combined with the Program.
+
+"Program" means the Contributions distributed in accordance with this 
Agreement.
+
+"Recipient" means anyone who receives the Program under this Agreement, 
including all Contributors.
+
+2. GRANT OF RIGHTS
+
+a) Subject to the terms of this Agreement, each Contributor hereby grants 
Recipient a non-exclusive, worldwide, royalty-free copyright license to 
reproduce, prepare derivative works of, publicly display, publicly perform, 
distribute and sublicense the Contribution of such Contributor, if any, and 
such derivative works, in source code and object code form.
+b) Subject to the terms of this Agreement, each Contributor hereby grants 
Recipient a non-exclusive, worldwide, royalty-free patent license under 
Licensed Patents to make, use, sell, offer to sell, import and otherwise 
transfer the Contribution of such Contributor, if any, in source code and 
object code form. This patent license shall apply to the combination of the 
Contribution and the Program if, at the time the Contribution is added by the 
Contributor, such addition of the Contributio [...]
+c) Recipient understands that although each Contributor grants the licenses to 
its Contributions set forth herein, no assurances are provided by any 
Contributor that the Program does not infringe the patent or other intellectual 
property rights of any other entity. Each Contributor disclaims any liability 
to Recipient for claims brought by any other entity based on infringement of 
intellectual property rights or otherwise. As a condition to exercising the 
rights and licenses granted here [...]
+d) Each Contributor represents that to its knowledge it has sufficient 
copyright rights in its Contribution, if any, to grant the copyright license 
set forth in this Agreement.
+3. REQUIREMENTS
+
+A Contributor may choose to distribute the Program in object code form under 
its own license agreement, provided that:
+
+a) it complies with the terms and conditions of this Agreement; and
+b) its license agreement:
+i) effectively disclaims on behalf of all Contributors all warranties and 
conditions, express and implied, including warranties or conditions of title 
and non-infringement, and implied warranties or conditions of merchantability 
and fitness for a particular purpose;
+ii) effectively excludes on behalf of all Contributors all liability for 
damages, including direct, indirect, special, incidental and consequential 
damages, such as lost profits;
+iii) states that any provisions which differ from this Agreement are offered 
by that Contributor alone and not by any other party; and
+iv) states that source code for the Program is available from such 
Contributor, and informs licensees how to obtain it in a reasonable manner on 
or through a medium customarily used for software exchange.
+When the Program is made available in source code form:
+
+a) it must be made available under this Agreement; and
+b) a copy of this Agreement must be included with each copy of the Program.
+Contributors may not remove or alter any copyright notices contained within 
the Program.
+
+Each Contributor must identify itself as the originator of its Contribution, 
if any, in a manner that reasonably allows subsequent Recipients to identify 
the originator of the Contribution.
+
+4. COMMERCIAL DISTRIBUTION
+
+Commercial distributors of software may accept certain responsibilities with 
respect to end users, business partners and the like. While this license is 
intended to facilitate the commercial use of the Program, the Contributor who 
includes the Program in a commercial product offering should do so in a manner 
which does not create potential liability for other Contributors. Therefore, if 
a Contributor includes the Program in a commercial product offering, such 
Contributor ("Commercial Con [...]
+
+For example, a Contributor might include the Program in a commercial product 
offering, Product X. That Contributor is then a Commercial Contributor. If that 
Commercial Contributor then makes performance claims, or offers warranties 
related to Product X, those performance claims and warranties are such 
Commercial Contributor's responsibility alone. Under this section, the 
Commercial Contributor would have to defend claims against the other 
Contributors related to those performance claims  [...]
+
+5. NO WARRANTY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN 
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR 
IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, 
NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each 
Recipient is solely responsible for determining the appropriateness of using 
and distributing the Program and assumes all risks associated with its exercise 
of rights under this Ag [...]
+
+6. DISCLAIMER OF LIABILITY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY 
CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST 
PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY 
WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS 
GRANTED HEREUNDER, EVEN IF [...]
+
+7. GENERAL
+
+If any provision of this Agreement is invalid or unenforceable under 
applicable law, it shall not affect the validity or enforceability of the 
remainder of the terms of this Agreement, and without further action by the 
parties hereto, such provision shall be reformed to the minimum extent 
necessary to make such provision valid and enforceable.
+
+If Recipient institutes patent litigation against any entity (including a 
cross-claim or counterclaim in a lawsuit) alleging that the Program itself 
(excluding combinations of the Program with other software or hardware) 
infringes such Recipient's patent(s), then such Recipient's rights granted 
under Section 2(b) shall terminate as of the date such litigation is filed.
+
+All Recipient's rights under this Agreement shall terminate if it fails to 
comply with any of the material terms or conditions of this Agreement and does 
not cure such failure in a reasonable period of time after becoming aware of 
such noncompliance. If all Recipient's rights under this Agreement terminate, 
Recipient agrees to cease use and distribution of the Program as soon as 
reasonably practicable. However, Recipient's obligations under this Agreement 
and any licenses granted by Reci [...]
+
+Everyone is permitted to copy and distribute copies of this Agreement, but in 
order to avoid inconsistency the Agreement is copyrighted and may only be 
modified in the following manner. The Agreement Steward reserves the right to 
publish new versions (including revisions) of this Agreement from time to time. 
No one other than the Agreement Steward has the right to modify this Agreement. 
The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation 
may assign the respons [...]
+
+This Agreement is governed by the laws of the State of New York and the 
intellectual property laws of the United States of America. No party to this 
Agreement will bring a legal action under this Agreement more than one year 
after the cause of action arose. Each party waives its rights to a jury trial 
in any resulting litigation.
\ No newline at end of file
diff --git 
a/fluss-lake/fluss-lake-lance/src/main/resources/META-INF/licenses/LICENSE.slf4j-api
 
b/fluss-lake/fluss-lake-lance/src/main/resources/META-INF/licenses/LICENSE.slf4j-api
new file mode 100644
index 000000000..93119e70e
--- /dev/null
+++ 
b/fluss-lake/fluss-lake-lance/src/main/resources/META-INF/licenses/LICENSE.slf4j-api
@@ -0,0 +1,21 @@
+Copyright (c) 2004-2017 QOS.ch
+ All rights reserved.
+
+ Permission is hereby granted, free  of charge, to any person obtaining
+ a  copy  of this  software  and  associated  documentation files  (the
+ "Software"), to  deal in  the Software without  restriction, including
+ without limitation  the rights to  use, copy, modify,  merge, publish,
+ distribute,  sublicense, and/or sell  copies of  the Software,  and to
+ permit persons to whom the Software  is furnished to do so, subject to
+ the following conditions:
+
+ The  above  copyright  notice  and  this permission  notice  shall  be
+ included in all copies or substantial portions of the Software.
+
+ THE  SOFTWARE IS  PROVIDED  "AS  IS", WITHOUT  WARRANTY  OF ANY  KIND,
+ EXPRESS OR  IMPLIED, INCLUDING  BUT NOT LIMITED  TO THE  WARRANTIES OF
+ MERCHANTABILITY,    FITNESS    FOR    A   PARTICULAR    PURPOSE    AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ OF CONTRACT, TORT OR OTHERWISE,  ARISING FROM, OUT OF OR IN CONNECTION
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git 
a/fluss-lake/fluss-lake-lance/src/main/resources/META-INF/licenses/LICENSE.zstd-jni
 
b/fluss-lake/fluss-lake-lance/src/main/resources/META-INF/licenses/LICENSE.zstd-jni
new file mode 100644
index 000000000..66abb8ae7
--- /dev/null
+++ 
b/fluss-lake/fluss-lake-lance/src/main/resources/META-INF/licenses/LICENSE.zstd-jni
@@ -0,0 +1,26 @@
+Zstd-jni: JNI bindings to Zstd Library
+
+Copyright (c) 2015-present, Luben Karavelov/ All rights reserved.
+
+BSD License
+
+Redistribution and use in source and binary forms, with or without 
modification,
+are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice, 
this
+  list of conditions and the following disclaimer in the documentation and/or
+  other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 
FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git 
a/fluss-lake/fluss-lake-lance/src/main/resources/META-INF/services/com.alibaba.fluss.lake.lakestorage.LakeStoragePlugin
 
b/fluss-lake/fluss-lake-lance/src/main/resources/META-INF/services/com.alibaba.fluss.lake.lakestorage.LakeStoragePlugin
new file mode 100644
index 000000000..693ee63cc
--- /dev/null
+++ 
b/fluss-lake/fluss-lake-lance/src/main/resources/META-INF/services/com.alibaba.fluss.lake.lakestorage.LakeStoragePlugin
@@ -0,0 +1,17 @@
+#
+# Copyright (c) 2025 Alibaba Group Holding Ltd.
+#
+# Licensed 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.
+#
+
+com.alibaba.fluss.lake.lance.LanceLakeStoragePlugin
\ No newline at end of file
diff --git 
a/fluss-lake/fluss-lake-lance/src/test/java/com/alibaba/fluss/lake/lance/LakeEnabledTableCreateITCase.java
 
b/fluss-lake/fluss-lake-lance/src/test/java/com/alibaba/fluss/lake/lance/LakeEnabledTableCreateITCase.java
new file mode 100644
index 000000000..e0c16e30d
--- /dev/null
+++ 
b/fluss-lake/fluss-lake-lance/src/test/java/com/alibaba/fluss/lake/lance/LakeEnabledTableCreateITCase.java
@@ -0,0 +1,204 @@
+/*
+ * 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 com.alibaba.fluss.lake.lance;
+
+import com.alibaba.fluss.client.Connection;
+import com.alibaba.fluss.client.ConnectionFactory;
+import com.alibaba.fluss.client.admin.Admin;
+import com.alibaba.fluss.config.ConfigOptions;
+import com.alibaba.fluss.config.Configuration;
+import com.alibaba.fluss.exception.FlussRuntimeException;
+import com.alibaba.fluss.lake.lance.utils.LanceDatasetAdapter;
+import com.alibaba.fluss.metadata.DataLakeFormat;
+import com.alibaba.fluss.metadata.Schema;
+import com.alibaba.fluss.metadata.TableDescriptor;
+import com.alibaba.fluss.metadata.TablePath;
+import com.alibaba.fluss.server.testutils.FlussClusterExtension;
+import com.alibaba.fluss.types.DataTypes;
+
+import org.apache.arrow.vector.types.DateUnit;
+import org.apache.arrow.vector.types.FloatingPointPrecision;
+import org.apache.arrow.vector.types.TimeUnit;
+import org.apache.arrow.vector.types.pojo.ArrowType;
+import org.apache.arrow.vector.types.pojo.Field;
+import org.apache.arrow.vector.types.pojo.FieldType;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import java.nio.file.Files;
+import java.util.Arrays;
+
+import static com.alibaba.fluss.metadata.TableDescriptor.BUCKET_COLUMN_NAME;
+import static com.alibaba.fluss.metadata.TableDescriptor.OFFSET_COLUMN_NAME;
+import static com.alibaba.fluss.metadata.TableDescriptor.TIMESTAMP_COLUMN_NAME;
+import static org.assertj.core.api.Assertions.assertThat;
+
+/** ITCase for create lake enabled table with lance as lake storage. */
+class LakeEnabledTableCreateITCase {
+
+    @RegisterExtension
+    public static final FlussClusterExtension FLUSS_CLUSTER_EXTENSION =
+            FlussClusterExtension.builder()
+                    .setNumOfTabletServers(3)
+                    .setClusterConf(initConfig())
+                    .build();
+
+    private static Configuration lanceConf;
+
+    private static final String DATABASE = "fluss";
+
+    private static final int BUCKET_NUM = 3;
+
+    private Connection conn;
+    private Admin admin;
+
+    @BeforeEach
+    protected void setup() {
+        conn = 
ConnectionFactory.createConnection(FLUSS_CLUSTER_EXTENSION.getClientConfig());
+        admin = conn.getAdmin();
+    }
+
+    @AfterEach
+    protected void teardown() throws Exception {
+        if (admin != null) {
+            admin.close();
+            admin = null;
+        }
+
+        if (conn != null) {
+            conn.close();
+            conn = null;
+        }
+    }
+
+    private static Configuration initConfig() {
+        Configuration conf = new Configuration();
+        lanceConf = new Configuration();
+        conf.set(ConfigOptions.DATALAKE_FORMAT, DataLakeFormat.LANCE);
+        conf.setString("datalake.format", "lance");
+        String warehousePath;
+        try {
+            warehousePath =
+                    Files.createTempDirectory("fluss-testing-datalake-enabled")
+                            .resolve("warehouse")
+                            .toString();
+        } catch (Exception e) {
+            throw new FlussRuntimeException("Failed to create warehouse path");
+        }
+        conf.setString("datalake.lance.warehouse", warehousePath);
+        lanceConf.setString("warehouse", warehousePath);
+        return conf;
+    }
+
+    @Test
+    void testLogTable() throws Exception {
+        // test bucket key log table
+        TableDescriptor logTable =
+                TableDescriptor.builder()
+                        .schema(
+                                Schema.newBuilder()
+                                        .column("log_c1", DataTypes.INT())
+                                        .column("log_c2", DataTypes.STRING())
+                                        .column("log_c3", DataTypes.TINYINT())
+                                        .column("log_c4", DataTypes.SMALLINT())
+                                        .column("log_c5", DataTypes.BIGINT())
+                                        .column("log_c6", DataTypes.BOOLEAN())
+                                        .column("log_c7", DataTypes.FLOAT())
+                                        .column("log_c8", DataTypes.DOUBLE())
+                                        .column("log_c9", DataTypes.CHAR(1))
+                                        .column("log_c10", 
DataTypes.BINARY(10))
+                                        .column("log_c11", DataTypes.BYTES())
+                                        .column("log_c12", 
DataTypes.DECIMAL(10, 2))
+                                        .column("log_c13", DataTypes.DATE())
+                                        .column("log_c14", DataTypes.TIME())
+                                        .column("log_c15", 
DataTypes.TIMESTAMP_LTZ())
+                                        .column("log_c16", 
DataTypes.TIMESTAMP())
+                                        .build())
+                        .property(ConfigOptions.TABLE_DATALAKE_ENABLED, true)
+                        .distributedBy(BUCKET_NUM, "log_c1", "log_c2")
+                        .build();
+        TablePath logTablePath = TablePath.of(DATABASE, "log_table");
+        admin.createTable(logTablePath, logTable, false).get();
+        LanceConfig config = LanceConfig.from(lanceConf.toMap(), DATABASE, 
"log_table");
+
+        // check the gotten log table
+        Field logC1 = new Field("log_c1", FieldType.nullable(new 
ArrowType.Int(4 * 8, true)), null);
+        Field logC2 = new Field("log_c2", FieldType.nullable(new 
ArrowType.Utf8()), null);
+        Field logC3 = new Field("log_c3", FieldType.nullable(new 
ArrowType.Int(8, true)), null);
+        Field logC4 = new Field("log_c4", FieldType.nullable(new 
ArrowType.Int(2 * 8, true)), null);
+        Field logC5 = new Field("log_c5", FieldType.nullable(new 
ArrowType.Int(8 * 8, true)), null);
+        Field logC6 = new Field("log_c6", FieldType.nullable(new 
ArrowType.Bool()), null);
+        Field logC7 =
+                new Field(
+                        "log_c7",
+                        FieldType.nullable(
+                                new 
ArrowType.FloatingPoint(FloatingPointPrecision.SINGLE)),
+                        null);
+        Field logC8 =
+                new Field(
+                        "log_c8",
+                        FieldType.nullable(
+                                new 
ArrowType.FloatingPoint(FloatingPointPrecision.DOUBLE)),
+                        null);
+        Field logC9 = new Field("log_c9", FieldType.nullable(new 
ArrowType.Utf8()), null);
+        Field logC10 =
+                new Field("log_c10", FieldType.nullable(new 
ArrowType.FixedSizeBinary(10)), null);
+        Field logC11 = new Field("log_c11", FieldType.nullable(new 
ArrowType.Binary()), null);
+        Field logC12 = new Field("log_c12", FieldType.nullable(new 
ArrowType.Decimal(10, 2)), null);
+        Field logC13 =
+                new Field("log_c13", FieldType.nullable(new 
ArrowType.Date(DateUnit.DAY)), null);
+        Field logC14 =
+                new Field(
+                        "log_c14",
+                        FieldType.nullable(new ArrowType.Time(TimeUnit.SECOND, 
32)),
+                        null);
+        Field logC15 =
+                new Field(
+                        "log_c15",
+                        FieldType.nullable(new 
ArrowType.Timestamp(TimeUnit.MICROSECOND, null)),
+                        null);
+        Field logC16 =
+                new Field(
+                        "log_c16",
+                        FieldType.nullable(new 
ArrowType.Timestamp(TimeUnit.MICROSECOND, null)),
+                        null);
+
+        // for __bucket, __offset, __timestamp
+        Field logC17 =
+                new Field(
+                        BUCKET_COLUMN_NAME, FieldType.nullable(new 
ArrowType.Int(32, true)), null);
+        Field logC18 =
+                new Field(
+                        OFFSET_COLUMN_NAME, FieldType.nullable(new 
ArrowType.Int(64, true)), null);
+        Field logC19 =
+                new Field(
+                        TIMESTAMP_COLUMN_NAME,
+                        FieldType.nullable(new 
ArrowType.Timestamp(TimeUnit.MICROSECOND, null)),
+                        null);
+
+        org.apache.arrow.vector.types.pojo.Schema expectedSchema =
+                new org.apache.arrow.vector.types.pojo.Schema(
+                        Arrays.asList(
+                                logC1, logC2, logC3, logC4, logC5, logC6, 
logC7, logC8, logC9,
+                                logC10, logC11, logC12, logC13, logC14, 
logC15, logC16, logC17,
+                                logC18, logC19));
+        
assertThat(expectedSchema).isEqualTo(LanceDatasetAdapter.getSchema(config).get());
+    }
+}
diff --git 
a/fluss-lake/fluss-lake-lance/src/test/resources/log4j2-test.properties 
b/fluss-lake/fluss-lake-lance/src/test/resources/log4j2-test.properties
new file mode 100644
index 000000000..fabc6bf5a
--- /dev/null
+++ b/fluss-lake/fluss-lake-lance/src/test/resources/log4j2-test.properties
@@ -0,0 +1,27 @@
+#
+# 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.
+#
+# Set root logger level to OFF to not flood build logs
+# set manually to INFO for debugging purposes
+rootLogger.level=OFF
+rootLogger.appenderRef.test.ref=TestLogger
+appender.testlogger.name=TestLogger
+appender.testlogger.type=CONSOLE
+appender.testlogger.target=SYSTEM_ERR
+appender.testlogger.layout.type=PatternLayout
+appender.testlogger.layout.pattern=%-4r [%t] %-5p %c %x - %m%n
+
diff --git 
a/fluss-lake/fluss-lake-lance/src/test/resources/org.junit.jupiter.api.extension.Extension
 
b/fluss-lake/fluss-lake-lance/src/test/resources/org.junit.jupiter.api.extension.Extension
new file mode 100644
index 000000000..590243ea8
--- /dev/null
+++ 
b/fluss-lake/fluss-lake-lance/src/test/resources/org.junit.jupiter.api.extension.Extension
@@ -0,0 +1,19 @@
+#
+# 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.
+#
+
+com.alibaba.fluss.testutils.common.TestLoggerExtension
\ No newline at end of file
diff --git a/fluss-lake/pom.xml b/fluss-lake/pom.xml
index dadae6d52..d84d10a0d 100644
--- a/fluss-lake/pom.xml
+++ b/fluss-lake/pom.xml
@@ -78,6 +78,7 @@
     <modules>
         <module>fluss-lake-paimon</module>
         <module>fluss-lake-iceberg</module>
+        <module>fluss-lake-lance</module>
     </modules>
     <packaging>pom</packaging>
 </project>
\ No newline at end of file
diff --git a/fluss-test-coverage/pom.xml b/fluss-test-coverage/pom.xml
index 97c35e6eb..48288186b 100644
--- a/fluss-test-coverage/pom.xml
+++ b/fluss-test-coverage/pom.xml
@@ -329,6 +329,8 @@
                                         
<exclude>com.alibaba.fluss.lake.batch.ArrowRecordBatch</exclude>
                                         
<exclude>com.alibaba.fluss.lake.committer.CommittedLakeSnapshot</exclude>
                                         
<exclude>com.alibaba.fluss.lake.paimon.FlussDataTypeToPaimonDataType</exclude>
+                                        <!-- start exclude for lake lance -->
+                                        
<exclude>com.alibaba.fluss.lake.lance.*</exclude>
                                         <!-- temporarily exclude iceberg -->
                                         
<exclude>com.alibaba.fluss.lake.iceberg.*</exclude>
                                         <!-- start exclude for flink tiering 
service -->

Reply via email to