Copilot commented on code in PR #3476:
URL: https://github.com/apache/fluss/pull/3476#discussion_r3400622515


##########
fluss-spark/fluss-spark-common/src/main/scala/org/apache/fluss/spark/SparkTable.scala:
##########
@@ -68,6 +68,10 @@ class SparkTable(
         flussConfig.get(SparkFlussConf.SCAN_START_UP_MODE))
       .toUpperCase
     val isFullMode = startupMode == SparkFlussConf.StartUpMode.FULL.toString
+    if (isDataLakeEnabled && 
LakeTableUtil.hasCustomLakePath(tableInfo.getProperties)) {
+      throw new UnsupportedOperationException(
+        "Custom lake table path is not supported in Spark connector yet.")
+    }

Review Comment:
   This currently rejects *any* Spark access when `table.datalake.enabled=true` 
and a custom lake path mapping is set, even for scan modes that may not touch 
the lake (e.g., non-FULL modes). The PR description says only Spark *lake 
reads* are rejected; consider narrowing the guard to the code paths that 
actually read from the lake (e.g., FULL mode / `$lake`-equivalent paths), or 
adjust the behavior/message to match the intended restriction.



##########
fluss-server/src/main/java/org/apache/fluss/server/zk/ZooKeeperClient.java:
##########
@@ -1365,6 +1365,16 @@ public Optional<LakeTable> getLakeTable(long tableId) 
throws Exception {
         return getOrEmpty(zkPath).map(LakeTableZNode::decode);
     }
 
+    /** Deletes the {@link LakeTable} for the given table ID if it exists. */
+    public void deleteLakeTable(long tableId) throws Exception {
+        String zkPath = LakeTableZNode.path(tableId);
+        try {
+            zkClient.delete().forPath(zkPath);
+        } catch (KeeperException.NoNodeException ignored) {
+            // Ignore if the lake table progress has not been committed yet.
+        }
+    }

Review Comment:
   Using `delete().forPath(...)` will fail if the lake-table znode ever gains 
children (now or in future schema evolutions). To make this deletion more 
robust, consider using Curator’s `deletingChildrenIfNeeded()` (and optionally 
`guaranteed()`) so cleanup does not unexpectedly fail due to node structure 
changes.



##########
fluss-server/src/main/java/org/apache/fluss/server/zk/data/lake/LakeTableHelper.java:
##########
@@ -79,6 +79,24 @@ public void registerLakeTableSnapshotV2(
         registerLakeTableSnapshotV2(tableId, lakeSnapshotMetadata, null);
     }
 
+    /** Clears the committed lake table progress for the given table. */
+    public void clearLakeTableProgress(long tableId) throws Exception {
+        Optional<LakeTable> optLakeTable = zkClient.getLakeTable(tableId);
+        if (!optLakeTable.isPresent()) {
+            return;
+        }
+
+        zkClient.deleteLakeTable(tableId);
+        LakeTable lakeTable = optLakeTable.get();
+        List<LakeTable.LakeSnapshotMetadata> lakeSnapshotMetadatas =
+                lakeTable.getLakeSnapshotMetadatas();
+        if (lakeSnapshotMetadatas != null) {
+            for (LakeTable.LakeSnapshotMetadata lakeSnapshotMetadata : 
lakeSnapshotMetadatas) {
+                lakeSnapshotMetadata.discard();
+            }
+        }
+    }

Review Comment:
   The ZK progress node is deleted before discarding snapshot metadata. If 
`discard()` throws (e.g., IO failure deleting offset files), the cluster loses 
the reference to the progress but may leak local/remote files. Consider 
discarding first and deleting the ZK node after successful cleanup, or use a 
try/finally pattern that attempts cleanup for all snapshots and only then 
deletes (or records) progress removal so failures don’t leave orphaned data.



##########
fluss-lake/fluss-lake-paimon/src/test/java/org/apache/fluss/lake/paimon/LakeEnabledTableCreateWithHiveCatalogITCase.java:
##########
@@ -0,0 +1,198 @@
+/*
+ * 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.paimon;
+
+import org.apache.fluss.client.Connection;
+import org.apache.fluss.client.ConnectionFactory;
+import org.apache.fluss.client.admin.Admin;
+import org.apache.fluss.config.ConfigOptions;
+import org.apache.fluss.config.Configuration;
+import org.apache.fluss.exception.FlussRuntimeException;
+import org.apache.fluss.exception.InvalidAlterTableException;
+import org.apache.fluss.metadata.Schema;
+import org.apache.fluss.metadata.TableChange;
+import org.apache.fluss.metadata.TableDescriptor;
+import org.apache.fluss.metadata.TableInfo;
+import org.apache.fluss.metadata.TablePath;
+import org.apache.fluss.server.testutils.FlussClusterExtension;
+import org.apache.fluss.types.DataTypes;
+
+import org.apache.paimon.catalog.AbstractCatalog;
+import org.apache.paimon.catalog.Catalog;
+import org.apache.paimon.catalog.CatalogContext;
+import org.apache.paimon.catalog.CatalogFactory;
+import org.apache.paimon.catalog.Identifier;
+import org.apache.paimon.options.Options;
+import org.apache.paimon.table.Table;
+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.net.URI;
+import java.nio.file.Files;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static 
org.apache.fluss.server.utils.LakeStorageUtils.extractLakeProperties;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+/** ITCase for create lake enabled table with Paimon Hive catalog. */
+class LakeEnabledTableCreateWithHiveCatalogITCase {
+
+    @RegisterExtension
+    public static final FlussClusterExtension FLUSS_CLUSTER_EXTENSION =
+            FlussClusterExtension.builder()
+                    .setNumOfTabletServers(3)
+                    .setClusterConf(initConfig())
+                    .build();
+
+    private static final String DATABASE = "fluss";
+    private static final int BUCKET_NUM = 3;
+
+    private static Catalog paimonCatalog;

Review Comment:
   The test creates a static `paimonCatalog` but never closes it. Some catalog 
implementations keep threads / open resources, which can lead to flaky builds 
or hanging test JVMs. Add an `@AfterAll` (or equivalent) to close 
`paimonCatalog` when the test class finishes to ensure deterministic cleanup.



##########
fluss-lake/fluss-lake-paimon/src/test/java/org/apache/fluss/lake/paimon/LakeEnabledTableCreateWithHiveCatalogITCase.java:
##########
@@ -0,0 +1,198 @@
+/*
+ * 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.paimon;
+
+import org.apache.fluss.client.Connection;
+import org.apache.fluss.client.ConnectionFactory;
+import org.apache.fluss.client.admin.Admin;
+import org.apache.fluss.config.ConfigOptions;
+import org.apache.fluss.config.Configuration;
+import org.apache.fluss.exception.FlussRuntimeException;
+import org.apache.fluss.exception.InvalidAlterTableException;
+import org.apache.fluss.metadata.Schema;
+import org.apache.fluss.metadata.TableChange;
+import org.apache.fluss.metadata.TableDescriptor;
+import org.apache.fluss.metadata.TableInfo;
+import org.apache.fluss.metadata.TablePath;
+import org.apache.fluss.server.testutils.FlussClusterExtension;
+import org.apache.fluss.types.DataTypes;
+
+import org.apache.paimon.catalog.AbstractCatalog;
+import org.apache.paimon.catalog.Catalog;
+import org.apache.paimon.catalog.CatalogContext;
+import org.apache.paimon.catalog.CatalogFactory;
+import org.apache.paimon.catalog.Identifier;
+import org.apache.paimon.options.Options;
+import org.apache.paimon.table.Table;
+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.net.URI;
+import java.nio.file.Files;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import static 
org.apache.fluss.server.utils.LakeStorageUtils.extractLakeProperties;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+/** ITCase for create lake enabled table with Paimon Hive catalog. */
+class LakeEnabledTableCreateWithHiveCatalogITCase {
+
+    @RegisterExtension
+    public static final FlussClusterExtension FLUSS_CLUSTER_EXTENSION =
+            FlussClusterExtension.builder()
+                    .setNumOfTabletServers(3)
+                    .setClusterConf(initConfig())
+                    .build();
+
+    private static final String DATABASE = "fluss";
+    private static final int BUCKET_NUM = 3;
+
+    private static Catalog paimonCatalog;
+    private static String warehousePath;
+    private static String customTablePath;
+
+    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();
+        conf.setString("datalake.format", "paimon");
+        conf.setString("datalake.paimon.metastore", "hive");
+        conf.setString("datalake.paimon.cache-enabled", "false");
+        try {
+            java.nio.file.Path baseDir = 
Files.createTempDirectory("fluss-testing-hms-paimon");
+            warehousePath = baseDir.resolve("warehouse").toString();
+            customTablePath = 
baseDir.resolve("custom_lake_table_path").toUri().toString();
+            conf.setString(
+                    "datalake.paimon.javax.jdo.option.ConnectionURL",
+                    "jdbc:derby:memory:" + baseDir.resolve("metastore_db") + 
";create=true");
+        } catch (Exception e) {
+            throw new FlussRuntimeException("Failed to create hive catalog 
test path", e);
+        }
+        conf.setString("datalake.paimon.warehouse", warehousePath);
+        conf.setString(
+                "datalake.paimon.javax.jdo.option.ConnectionDriverName",
+                "org.apache.derby.jdbc.EmbeddedDriver");
+        conf.setString("datalake.paimon.datanucleus.schema.autoCreateAll", 
"true");
+        conf.setString("datalake.paimon.hive.metastore.schema.verification", 
"false");
+
+        paimonCatalog =
+                CatalogFactory.createCatalog(
+                        
CatalogContext.create(Options.fromMap(extractLakeProperties(conf))));

Review Comment:
   The test creates a static `paimonCatalog` but never closes it. Some catalog 
implementations keep threads / open resources, which can lead to flaky builds 
or hanging test JVMs. Add an `@AfterAll` (or equivalent) to close 
`paimonCatalog` when the test class finishes to ensure deterministic cleanup.



##########
pom.xml:
##########
@@ -105,6 +105,12 @@
         <paimon.version>1.3.1</paimon.version>
         <iceberg.version>1.10.1</iceberg.version>
         <hudi.version>1.1.0</hudi.version>
+        <hive.version>2.3.10</hive.version>
+        <datanucleus-api-jdo.version>4.2.4</datanucleus-api-jdo.version>
+        <datanucleus-core.version>4.1.17</datanucleus-core.version>
+        <datanucleus-rdbms.version>4.1.19</datanucleus-rdbms.version>
+        <javax-jdo.version>3.2.0-m3</javax-jdo.version>
+        <derby.version>10.14.2.0</derby.version>

Review Comment:
   Using a milestone artifact version (`3.2.0-m3`) for `javax.jdo` can reduce 
build reproducibility and may be less stable than a final release. Consider 
switching to a stable release version (if available/compatible) to avoid 
transient resolution issues and reduce dependency risk, especially since these 
are brought in to support tests.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to