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


##########
fluss-server/src/main/java/org/apache/fluss/server/coordinator/LakeCatalogDynamicLoader.java:
##########
@@ -63,17 +66,35 @@ public void validate(Configuration newConfig) throws 
ConfigException {
                 newConfig.getOptional(DATALAKE_FORMAT).isPresent()
                         ? newConfig.get(DATALAKE_FORMAT)
                         : currentConfiguration.get(DATALAKE_FORMAT);
+
+        // TODO: validate(...) only sees the merged effective cluster config, 
so it cannot
+        // detect the case where a user enables datalake.enabled and unsets

Review Comment:
   LakeCatalogDynamicLoader.validate(): `newDatalakeFormat` falls back to 
`currentConfiguration.get(DATALAKE_FORMAT)` when the new effective config has 
no `datalake.format`. That prevents the intended “format is null => skip prefix 
validation” behavior and can block disabling `datalake.format` if any 
`datalake.*` keys from a different format are present. Since `newConfig` is 
already the merged effective config, derive `newDatalakeFormat` only from 
`newConfig` (no fallback to current).
   ```suggestion
                   newConfig.getOptional(DATALAKE_FORMAT).orElse(null);
   
           // TODO: validate(...) only sees the merged effective cluster 
config, so it cannot
           // detect the case where a user enables datalake.enabled and unsets
           // detect the case where a user enables datalake.enabled and unsets
   ```



##########
fluss-server/src/main/java/org/apache/fluss/server/utils/TableDescriptorValidation.java:
##########
@@ -118,6 +124,23 @@ public static void validateTableDescriptor(TableDescriptor 
tableDescriptor, int
         checkTieredLog(tableConf);
         checkPartition(tableConf, tableDescriptor.getPartitionKeys(), 
schema.getRowType());
         checkSystemColumns(schema.getRowType());
+        checkTableLakeFormatMatchesCluster(tableConf, clusterDataLakeFormat);
+    }
+
+    private static void checkTableLakeFormatMatchesCluster(
+            Configuration tableConf, @Nullable DataLakeFormat 
clusterDataLakeFormat) {
+        Optional<DataLakeFormat> tableDataLakeFormat =
+                tableConf.getOptional(ConfigOptions.TABLE_DATALAKE_FORMAT);
+        if (tableDataLakeFormat.isPresent() && tableDataLakeFormat.get() != 
clusterDataLakeFormat) {
+            throw new InvalidConfigException(
+                    String.format(
+                            "'%s' ('%s') must match cluster '%s' ('%s') when 
'%s' is enabled.",
+                            ConfigOptions.TABLE_DATALAKE_FORMAT.key(),
+                            tableDataLakeFormat.get(),
+                            ConfigOptions.DATALAKE_FORMAT.key(),
+                            clusterDataLakeFormat,
+                            ConfigOptions.TABLE_DATALAKE_ENABLED.key()));

Review Comment:
   checkTableLakeFormatMatchesCluster(): this rejects creating/altering a table 
that sets `table.datalake.format` when the cluster has no `datalake.format` 
configured (`clusterDataLakeFormat == null`). That breaks the 
documented/IT-case flow of pre-setting a table’s lake format before the cluster 
enables lakehouse. Only enforce the match when the cluster has a non-null 
`datalake.format` (and adjust the error message accordingly).
   ```suggestion
           if (clusterDataLakeFormat != null
                   && tableDataLakeFormat.isPresent()
                   && tableDataLakeFormat.get() != clusterDataLakeFormat) {
               throw new InvalidConfigException(
                       String.format(
                               "'%s' ('%s') must match cluster '%s' ('%s') when 
the cluster has a"
                                       + " datalake format configured.",
                               ConfigOptions.TABLE_DATALAKE_FORMAT.key(),
                               tableDataLakeFormat.get(),
                               ConfigOptions.DATALAKE_FORMAT.key(),
                               clusterDataLakeFormat));
   ```



##########
fluss-server/src/main/java/org/apache/fluss/server/coordinator/LakeCatalogDynamicLoader.java:
##########
@@ -63,17 +66,35 @@ public void validate(Configuration newConfig) throws 
ConfigException {
                 newConfig.getOptional(DATALAKE_FORMAT).isPresent()
                         ? newConfig.get(DATALAKE_FORMAT)
                         : currentConfiguration.get(DATALAKE_FORMAT);
+
+        // TODO: validate(...) only sees the merged effective cluster config, 
so it cannot
+        // detect the case where a user enables datalake.enabled and unsets
+        // datalake.format in the same dynamic config change. This may leave 
the cluster
+        // with datalake.enabled set but no datalake.format. Fixing this 
likely requires
+        // extending the validate/reconfigure framework to expose the 
incremental change
+        // set, rather than only the merged result. We accept this for now 
because
+        // table-level enablement is still validated, and enabling datalake 
for a table
+        // will fail if datalake.format is not configured.
+        boolean explicitDataLakeEnabled = 
newConfig.getOptional(DATALAKE_ENABLED).orElse(false);
+        if (explicitDataLakeEnabled && newDatalakeFormat != null) {
+            throw new ConfigException(
+                    String.format(
+                            "'%s' must be configured when '%s' is explicitly 
set.",
+                            DATALAKE_FORMAT.key(), DATALAKE_ENABLED.key()));
+        }

Review Comment:
   LakeCatalogDynamicLoader.validate(): the check for requiring datalake.format 
when datalake.enabled is explicitly configured is currently inverted and also 
only triggers when the value is `true`. As written, it throws when a format 
*is* present and misses the intended cases (e.g., `datalake.enabled=false` with 
missing format). Update the logic to detect explicit configuration via 
`newConfig.getOptional(DATALAKE_ENABLED).isPresent()` (not the boolean value) 
and throw only when the effective `datalake.format` is absent.



##########
website/docs/maintenance/operations/upgrade-notes-0.10.md:
##########
@@ -4,3 +4,36 @@ sidebar_position: 4
 ---
 
 # Upgrade Notes from v0.9 to v0.10
+
+## New `datalake.enabled` Cluster Configuration
+
+Starting from v0.10, Fluss introduces the cluster-level configuration 
`datalake.enabled` to control whether the cluster is ready to create and manage 
lakehouse tables.
+
+### Behavior Changes
+
+- If `datalake.enabled` is unset, Fluss keeps the legacy behavior: configuring 
`datalake.format` alone also enables lakehouse tables.
+- If `datalake.enabled = false`, Fluss only pre-binds the lake format for 
newly created tables and does not allow lakehouse tables yet.
+- If `datalake.enabled = true`, Fluss fully enables lakehouse tables.

Review Comment:
   PR description says “Documentation: no”, but this PR adds/updates multiple 
docs pages (quickstart, tiered-storage, upgrade notes, updating-configs, 
observability quickstart). Please update the PR description (or the 
“Documentation” section) to reflect that documentation was changed so 
reviewers/users don’t miss it.



##########
fluss-server/src/main/java/org/apache/fluss/server/coordinator/LakeCatalogDynamicLoader.java:
##########
@@ -136,19 +175,31 @@ public LakeCatalogContainer(
                 Configuration configuration,
                 @Nullable PluginManager pluginManager,
                 boolean isCoordinator) {
+            this.clusterDataLakeTableEnabled = 
isClusterDataLakeTableEnabled(configuration);
             this.dataLakeFormat = 
configuration.getOptional(DATALAKE_FORMAT).orElse(null);
             this.lakeCatalog =
                     isCoordinator ? createLakeCatalog(configuration, 
pluginManager) : null;
             this.defaultTableLakeOptions =
                     
LakeStorageUtils.generateDefaultTableLakeOptions(configuration);
-            if (isCoordinator && ((dataLakeFormat == null) != (lakeCatalog == 
null))) {
+            if (isCoordinator && clusterDataLakeTableEnabled == (lakeCatalog 
== null)) {
                 throw new ConfigException(
                         String.format(
-                                "dataLakeFormat and lakeCatalog must both be 
null or both non-null, but dataLakeFormat is %s, lakeCatalog is %s.",
-                                dataLakeFormat, lakeCatalog));
+                                "clusterDataLakeTableEnabled and lakeCatalog 
must both be false/null or true/non-null, but clusterDataLakeTableEnabled is 
%s, lakeCatalog is %s.",
+                                clusterDataLakeTableEnabled, lakeCatalog));
             }
         }
 
+        static boolean isClusterDataLakeTableEnabled(Configuration 
configuration) {
+            Optional<Boolean> explicitDataLakeEnabled = 
configuration.getOptional(DATALAKE_ENABLED);
+            // if datalake.enabled no set, use datalake.format for legacy 
cluster behavior

Review Comment:
   Minor grammar: comment says “if datalake.enabled no set”; should be “not 
set”.
   ```suggestion
               // if datalake.enabled not set, use datalake.format for legacy 
cluster behavior
   ```



-- 
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