This is an automated email from the ASF dual-hosted git repository.
czy006 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/amoro.git
The following commit(s) were added to refs/heads/master by this push:
new b7f7de31f [AMORO-4099] Add table-summary metric collection option for
non-optimizing tables (#4101)
b7f7de31f is described below
commit b7f7de31fb1815a9d022d0d76383e9f169e1f955
Author: Jiwon Park <[email protected]>
AuthorDate: Wed Apr 15 16:39:46 2026 +0900
[AMORO-4099] Add table-summary metric collection option for non-optimizing
tables (#4101)
* [AMORO-4099] Add table-summary metric collection option for
non-optimizing tables
- Allow collecting table_summary metrics when self-optimizing is disabled
by setting table-summary.enabled=true
- Fix periodic collection bug: remove optimizingNotNecessary() call in
summary-only branch to prevent snapshot gate from blocking subsequent
collections
- Separate property key from self-optimizing prefix:
self-optimizing.table-summary.enabled -> table-summary.enabled
- Add debug logging for table summary collection path
- Add comprehensive test coverage for summary-only mode
Signed-off-by: Jiwon Park <[email protected]>
* Fix review issues: remove double-append bug and redundant
tableSummaryOnly gate
- Remove duplicate newAppend().commit() in test appendData() since
OptimizingTestHelpers.appendBase() already commits
- Remove unused AppendFiles import
- Remove redundant tableSummaryOnly bypass in execute() since
snapshotChanged already covers unoptimized data
- Fix misleading test comment
Signed-off-by: Jiwon Park <[email protected]>
* Fix property key comment to match actual table property name
Signed-off-by: Jiwon Park <[email protected]>
---------
Signed-off-by: Jiwon Park <[email protected]>
Co-authored-by: ConradJam <[email protected]>
---
.../inline/TableRuntimeRefreshExecutor.java | 37 +-
.../amoro/server/table/TableConfigurations.java | 5 +
.../table/TestTableSummaryWithoutOptimizing.java | 412 +++++++++++++++++++++
.../org/apache/amoro/config/OptimizingConfig.java | 15 +
.../org/apache/amoro/table/TableProperties.java | 4 +
5 files changed, 462 insertions(+), 11 deletions(-)
diff --git
a/amoro-ams/src/main/java/org/apache/amoro/server/scheduler/inline/TableRuntimeRefreshExecutor.java
b/amoro-ams/src/main/java/org/apache/amoro/server/scheduler/inline/TableRuntimeRefreshExecutor.java
index b6b7e4ba6..ae9a1ea1d 100644
---
a/amoro-ams/src/main/java/org/apache/amoro/server/scheduler/inline/TableRuntimeRefreshExecutor.java
+++
b/amoro-ams/src/main/java/org/apache/amoro/server/scheduler/inline/TableRuntimeRefreshExecutor.java
@@ -70,11 +70,10 @@ public class TableRuntimeRefreshExecutor extends
PeriodicTableScheduler {
}
private boolean tryEvaluatingPendingInput(DefaultTableRuntime tableRuntime,
MixedTable table) {
- // only evaluate pending input when optimizing is enabled and in idle state
OptimizingConfig optimizingConfig = tableRuntime.getOptimizingConfig();
boolean optimizingEnabled = optimizingConfig.isEnabled();
if (optimizingEnabled &&
tableRuntime.getOptimizingStatus().equals(OptimizingStatus.IDLE)) {
-
+ // Evaluate pending input and collect table summary when optimizing is
enabled and idle
if (optimizingConfig.isMetadataBasedTriggerEnabled()
&& !MetadataBasedEvaluationEvent.isEvaluatingNecessary(
optimizingConfig, table, tableRuntime.getLastPlanTime())) {
@@ -102,11 +101,21 @@ public class TableRuntimeRefreshExecutor extends
PeriodicTableScheduler {
tableRuntime.setTableSummary(evaluator.getPendingInput());
return evaluatorIsNecessary;
+ } else if (!optimizingEnabled && optimizingConfig.isTableSummaryEnabled())
{
+ // Collect table summary metrics even when optimizing is disabled
+ logger.debug(
+ "{} collecting table summary (optimizing disabled, tableSummary
enabled)",
+ tableRuntime.getTableIdentifier());
+ AbstractOptimizingEvaluator evaluator =
+ IcebergTableUtil.createOptimizingEvaluator(tableRuntime, table,
maxPendingPartitions);
+ AbstractOptimizingEvaluator.PendingInput summary =
evaluator.getPendingInput();
+ logger.debug("{} table summary collected: {}",
tableRuntime.getTableIdentifier(), summary);
+ tableRuntime.setTableSummary(summary);
+ return false;
} else if (!optimizingEnabled) {
logger.debug(
"{} optimizing is not enabled, skip evaluating pending input",
tableRuntime.getTableIdentifier());
- // indicates no optimization demand now
return false;
} else {
logger.debug(
@@ -147,21 +156,27 @@ public class TableRuntimeRefreshExecutor extends
PeriodicTableScheduler {
AmoroTable<?> table = loadTable(tableRuntime);
defaultTableRuntime.refresh(table);
MixedTable mixedTable = (MixedTable) table.originalTable();
- // Check if there is any optimizing demand now.
+ boolean snapshotChanged =
+ (mixedTable.isKeyedTable()
+ && (lastOptimizedSnapshotId !=
defaultTableRuntime.getCurrentSnapshotId()
+ || lastOptimizedChangeSnapshotId
+ != defaultTableRuntime.getCurrentChangeSnapshotId()))
+ || (mixedTable.isUnkeyedTable()
+ && lastOptimizedSnapshotId !=
defaultTableRuntime.getCurrentSnapshotId());
+ OptimizingConfig optimizingConfig =
defaultTableRuntime.getOptimizingConfig();
+ boolean tableSummaryOnly =
+ !optimizingConfig.isEnabled() &&
optimizingConfig.isTableSummaryEnabled();
boolean hasOptimizingDemand = false;
- if ((mixedTable.isKeyedTable()
- && (lastOptimizedSnapshotId !=
defaultTableRuntime.getCurrentSnapshotId()
- || lastOptimizedChangeSnapshotId
- != defaultTableRuntime.getCurrentChangeSnapshotId()))
- || (mixedTable.isUnkeyedTable()
- && lastOptimizedSnapshotId !=
defaultTableRuntime.getCurrentSnapshotId())) {
+ if (snapshotChanged) {
hasOptimizingDemand = tryEvaluatingPendingInput(defaultTableRuntime,
mixedTable);
} else {
logger.debug("{} optimizing is not necessary",
defaultTableRuntime.getTableIdentifier());
}
// Update adaptive interval according to evaluated result.
- if
(defaultTableRuntime.getOptimizingConfig().isRefreshTableAdaptiveEnabled(interval))
{
+ // Skip adaptive interval for table-summary-only mode to maintain fixed
collection interval.
+ if (!tableSummaryOnly
+ &&
defaultTableRuntime.getOptimizingConfig().isRefreshTableAdaptiveEnabled(interval))
{
defaultTableRuntime.setLatestEvaluatedNeedOptimizing(hasOptimizingDemand);
long newInterval = getAdaptiveExecutingInterval(defaultTableRuntime);
defaultTableRuntime.setLatestRefreshInterval(newInterval);
diff --git
a/amoro-ams/src/main/java/org/apache/amoro/server/table/TableConfigurations.java
b/amoro-ams/src/main/java/org/apache/amoro/server/table/TableConfigurations.java
index 1601166db..2c403ebf3 100644
---
a/amoro-ams/src/main/java/org/apache/amoro/server/table/TableConfigurations.java
+++
b/amoro-ams/src/main/java/org/apache/amoro/server/table/TableConfigurations.java
@@ -232,6 +232,11 @@ public class TableConfigurations {
properties,
TableProperties.ENABLE_SELF_OPTIMIZING,
TableProperties.ENABLE_SELF_OPTIMIZING_DEFAULT))
+ .setTableSummaryEnabled(
+ CompatiblePropertyUtil.propertyAsBoolean(
+ properties,
+ TableProperties.TABLE_SUMMARY_ENABLED,
+ TableProperties.TABLE_SUMMARY_ENABLED_DEFAULT))
.setAllowPartialCommit(
CompatiblePropertyUtil.propertyAsBoolean(
properties,
diff --git
a/amoro-ams/src/test/java/org/apache/amoro/server/table/TestTableSummaryWithoutOptimizing.java
b/amoro-ams/src/test/java/org/apache/amoro/server/table/TestTableSummaryWithoutOptimizing.java
new file mode 100644
index 000000000..c3a616336
--- /dev/null
+++
b/amoro-ams/src/test/java/org/apache/amoro/server/table/TestTableSummaryWithoutOptimizing.java
@@ -0,0 +1,412 @@
+/*
+ * 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.amoro.server.table;
+
+import static
org.apache.amoro.server.table.TableSummaryMetrics.TABLE_SUMMARY_HEALTH_SCORE;
+import static
org.apache.amoro.server.table.TableSummaryMetrics.TABLE_SUMMARY_TOTAL_FILES;
+import static
org.apache.amoro.server.table.TableSummaryMetrics.TABLE_SUMMARY_TOTAL_FILES_SIZE;
+import static
org.apache.amoro.server.table.TableSummaryMetrics.TABLE_SUMMARY_TOTAL_RECORDS;
+
+import org.apache.amoro.BasicTableTestHelper;
+import org.apache.amoro.ServerTableIdentifier;
+import org.apache.amoro.TableFormat;
+import org.apache.amoro.TableTestHelper;
+import org.apache.amoro.catalog.BasicCatalogTestHelper;
+import org.apache.amoro.catalog.CatalogTestHelper;
+import org.apache.amoro.config.OptimizingConfig;
+import org.apache.amoro.io.MixedDataTestHelpers;
+import org.apache.amoro.metrics.Gauge;
+import org.apache.amoro.metrics.Metric;
+import org.apache.amoro.metrics.MetricDefine;
+import org.apache.amoro.metrics.MetricKey;
+import org.apache.amoro.server.manager.MetricManager;
+import org.apache.amoro.server.optimizing.OptimizingTestHelpers;
+import org.apache.amoro.server.scheduler.inline.TableRuntimeRefreshExecutor;
+import org.apache.amoro.shade.guava32.com.google.common.collect.ImmutableMap;
+import org.apache.amoro.shade.guava32.com.google.common.collect.Lists;
+import org.apache.amoro.table.MixedTable;
+import org.apache.amoro.table.TableProperties;
+import org.apache.amoro.table.UnkeyedTable;
+import org.apache.iceberg.DataFile;
+import org.apache.iceberg.DeleteFile;
+import org.apache.iceberg.data.Record;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.jupiter.api.Assertions;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/** Tests that table_summary metrics are collected when self-optimizing is
disabled. */
+@RunWith(Parameterized.class)
+public class TestTableSummaryWithoutOptimizing extends AMSTableTestBase {
+
+ @Parameterized.Parameters(name = "{0}, {1}")
+ public static Object[] parameters() {
+ return new Object[][] {
+ {new BasicCatalogTestHelper(TableFormat.ICEBERG), new
BasicTableTestHelper(true, false)}
+ };
+ }
+
+ public TestTableSummaryWithoutOptimizing(
+ CatalogTestHelper catalogTestHelper, TableTestHelper tableTestHelper) {
+ super(catalogTestHelper, tableTestHelper, false);
+ }
+
+ @Before
+ public void prepare() {
+ createDatabase();
+ createTable();
+ }
+
+ @After
+ public void clear() {
+ try {
+ dropTable();
+ dropDatabase();
+ } catch (Exception e) {
+ // ignore
+ }
+ }
+
+ private UnkeyedTable loadUnkeyedTable() {
+ return ((MixedTable)
tableService().loadTable(serverTableIdentifier()).originalTable())
+ .asUnkeyedTable();
+ }
+
+ private void initTableWithFiles() {
+ UnkeyedTable table = loadUnkeyedTable();
+ appendData(table);
+ appendPosDelete(table);
+ DefaultTableRuntime runtime =
getDefaultTableRuntime(serverTableIdentifier().getId());
+ runtime.refresh(tableService().loadTable(serverTableIdentifier()));
+ }
+
+ private void appendData(UnkeyedTable table) {
+ ArrayList<Record> records =
+ Lists.newArrayList(
+ tableTestHelper().generateTestRecord(1, "111", 0,
"2022-01-01T12:00:00"),
+ tableTestHelper().generateTestRecord(2, "222", 0,
"2022-01-01T12:00:00"));
+ OptimizingTestHelpers.appendBase(
+ table, tableTestHelper().writeBaseStore(table, 0L, records, false));
+ }
+
+ private void appendPosDelete(UnkeyedTable table) {
+ ArrayList<Record> records =
+ Lists.newArrayList(
+ tableTestHelper().generateTestRecord(3, "333", 0,
"2022-01-01T12:00:00"),
+ tableTestHelper().generateTestRecord(4, "444", 0,
"2022-01-01T12:00:00"));
+ List<DataFile> dataFiles =
+ OptimizingTestHelpers.appendBase(
+ table, tableTestHelper().writeBaseStore(table, 0L, records,
false));
+ List<DeleteFile> posDeleteFiles = Lists.newArrayList();
+ for (DataFile dataFile : dataFiles) {
+ posDeleteFiles.addAll(
+ MixedDataTestHelpers.writeBaseStorePosDelete(
+ table, 0L, dataFile, Collections.singletonList(0L)));
+ }
+ OptimizingTestHelpers.appendBasePosDelete(table, posDeleteFiles);
+ }
+
+ private void refreshPending() {
+ TableRuntimeRefreshExecutor refresher =
+ new TableRuntimeRefreshExecutor(tableService(), 1, Integer.MAX_VALUE,
Integer.MAX_VALUE);
+ refresher.execute(getDefaultTableRuntime(serverTableIdentifier().getId()));
+ refresher.dispose();
+ }
+
+ @SuppressWarnings("unchecked")
+ private Gauge<Long> getMetric(
+ Map<MetricKey, Metric> metrics, ServerTableIdentifier identifier,
MetricDefine define) {
+ return (Gauge<Long>)
+ metrics.get(
+ new MetricKey(
+ define,
+ ImmutableMap.of(
+ "catalog",
+ identifier.getCatalog(),
+ "database",
+ identifier.getDatabase(),
+ "table",
+ identifier.getTableName())));
+ }
+
+ @Test
+ public void testSummaryCollectedWhenOptimizingDisabledAndSummaryEnabled() {
+ // First, add files with optimizing enabled (default), then verify metrics
via refresh
+ initTableWithFiles();
+ refreshPending();
+
+ Map<MetricKey, Metric> metrics =
MetricManager.getInstance().getGlobalRegistry().getMetrics();
+ ServerTableIdentifier identifier = serverTableIdentifier();
+
+ Gauge<Long> totalFiles = getMetric(metrics, identifier,
TABLE_SUMMARY_TOTAL_FILES);
+ Gauge<Long> totalSize = getMetric(metrics, identifier,
TABLE_SUMMARY_TOTAL_FILES_SIZE);
+ Gauge<Long> totalRecords = getMetric(metrics, identifier,
TABLE_SUMMARY_TOTAL_RECORDS);
+ Gauge<Long> healthScore = getMetric(metrics, identifier,
TABLE_SUMMARY_HEALTH_SCORE);
+
+ // Baseline: metrics should be populated with optimizing enabled
+ long baselineTotalFiles = totalFiles.getValue();
+ long baselineTotalSize = totalSize.getValue();
+ Assertions.assertTrue(baselineTotalFiles > 0, "baseline totalFiles should
be > 0");
+ Assertions.assertTrue(baselineTotalSize > 0, "baseline totalSize should be
> 0");
+
+ // Now disable optimizing and explicitly enable table-summary
+ UnkeyedTable table = loadUnkeyedTable();
+ table
+ .updateProperties()
+ .set(TableProperties.ENABLE_SELF_OPTIMIZING, "false")
+ .set(TableProperties.TABLE_SUMMARY_ENABLED, "true")
+ .commit();
+ DefaultTableRuntime runtime =
getDefaultTableRuntime(serverTableIdentifier().getId());
+ runtime.refresh(tableService().loadTable(serverTableIdentifier()));
+
+ // Append more data to create a new snapshot
+ appendData(loadUnkeyedTable());
+ runtime.refresh(tableService().loadTable(serverTableIdentifier()));
+
+ // Execute refresh - should collect summary via summary-only branch
+ refreshPending();
+
+ // Verify: metrics should be updated (totalFiles/totalSize should increase
from new data)
+ Assertions.assertTrue(
+ totalFiles.getValue() >= baselineTotalFiles,
+ "totalFiles should be >= baseline after summary-only refresh");
+ Assertions.assertTrue(
+ totalSize.getValue() >= baselineTotalSize,
+ "totalSize should be >= baseline after summary-only refresh");
+ Assertions.assertTrue(totalRecords.getValue() > 0, "totalRecords should be
> 0");
+ Assertions.assertTrue(healthScore.getValue() >= 0, "healthScore should be
>= 0");
+ }
+
+ @Test
+ public void testSummaryNotCollectedWhenOptimizingDisabledWithDefault() {
+ // Disable optimizing only — table-summary.enabled defaults to false
+ UnkeyedTable table = loadUnkeyedTable();
+ table.updateProperties().set(TableProperties.ENABLE_SELF_OPTIMIZING,
"false").commit();
+ DefaultTableRuntime runtime =
getDefaultTableRuntime(serverTableIdentifier().getId());
+ runtime.refresh(tableService().loadTable(serverTableIdentifier()));
+
+ // Add files and refresh
+ initTableWithFiles();
+ refreshPending();
+
+ Map<MetricKey, Metric> metrics =
MetricManager.getInstance().getGlobalRegistry().getMetrics();
+ ServerTableIdentifier identifier = serverTableIdentifier();
+
+ Gauge<Long> totalFiles = getMetric(metrics, identifier,
TABLE_SUMMARY_TOTAL_FILES);
+ Gauge<Long> healthScore = getMetric(metrics, identifier,
TABLE_SUMMARY_HEALTH_SCORE);
+
+ // Metrics should remain at initial values (not collected)
+ Assertions.assertEquals(0, totalFiles.getValue(), "totalFiles should
remain 0");
+ Assertions.assertEquals(-1, healthScore.getValue(), "healthScore should
remain at initial -1");
+ }
+
+ @Test
+ public void testSummaryCollectedRepeatedlyWithoutNewSnapshots() {
+ // Setup: add files, then switch to summary-only mode
+ initTableWithFiles();
+ UnkeyedTable table = loadUnkeyedTable();
+ table
+ .updateProperties()
+ .set(TableProperties.ENABLE_SELF_OPTIMIZING, "false")
+ .set(TableProperties.TABLE_SUMMARY_ENABLED, "true")
+ .commit();
+ DefaultTableRuntime runtime =
getDefaultTableRuntime(serverTableIdentifier().getId());
+ runtime.refresh(tableService().loadTable(serverTableIdentifier()));
+
+ // First refresh — should collect summary
+ refreshPending();
+
+ Map<MetricKey, Metric> metrics =
MetricManager.getInstance().getGlobalRegistry().getMetrics();
+ ServerTableIdentifier identifier = serverTableIdentifier();
+ Gauge<Long> totalFiles = getMetric(metrics, identifier,
TABLE_SUMMARY_TOTAL_FILES);
+ Gauge<Long> healthScore = getMetric(metrics, identifier,
TABLE_SUMMARY_HEALTH_SCORE);
+
+ long firstTotalFiles = totalFiles.getValue();
+ long firstHealthScore = healthScore.getValue();
+ Assertions.assertTrue(firstTotalFiles > 0, "first refresh should collect
totalFiles > 0");
+ Assertions.assertTrue(firstHealthScore >= 0, "first refresh should collect
healthScore >= 0");
+
+ // Second refresh without any new snapshot — still collected because data
was never optimized
+ refreshPending();
+
+ Assertions.assertEquals(
+ firstTotalFiles,
+ totalFiles.getValue(),
+ "totalFiles should remain the same after second refresh without new
snapshot");
+ Assertions.assertEquals(
+ firstHealthScore,
+ healthScore.getValue(),
+ "healthScore should remain the same after second refresh without new
snapshot");
+
+ // Third refresh — still works
+ refreshPending();
+
+ Assertions.assertEquals(
+ firstTotalFiles,
+ totalFiles.getValue(),
+ "totalFiles should remain the same after third refresh without new
snapshot");
+ }
+
+ @Test
+ public void testSummaryUpdatedAfterNewSnapshotInSummaryOnlyMode() {
+ // Setup: add initial files
+ initTableWithFiles();
+
+ // Switch to summary-only mode
+ UnkeyedTable table = loadUnkeyedTable();
+ table
+ .updateProperties()
+ .set(TableProperties.ENABLE_SELF_OPTIMIZING, "false")
+ .set(TableProperties.TABLE_SUMMARY_ENABLED, "true")
+ .commit();
+ DefaultTableRuntime runtime =
getDefaultTableRuntime(serverTableIdentifier().getId());
+ runtime.refresh(tableService().loadTable(serverTableIdentifier()));
+
+ // First refresh — record baseline
+ refreshPending();
+
+ Map<MetricKey, Metric> metrics =
MetricManager.getInstance().getGlobalRegistry().getMetrics();
+ ServerTableIdentifier identifier = serverTableIdentifier();
+ Gauge<Long> totalFiles = getMetric(metrics, identifier,
TABLE_SUMMARY_TOTAL_FILES);
+ Gauge<Long> totalSize = getMetric(metrics, identifier,
TABLE_SUMMARY_TOTAL_FILES_SIZE);
+
+ long baselineTotalFiles = totalFiles.getValue();
+ long baselineTotalSize = totalSize.getValue();
+ Assertions.assertTrue(baselineTotalFiles > 0, "baseline totalFiles should
be > 0");
+
+ // Append more data — creates a new snapshot
+ appendData(loadUnkeyedTable());
+ runtime.refresh(tableService().loadTable(serverTableIdentifier()));
+
+ // Refresh again — metrics should reflect the additional files
+ refreshPending();
+
+ Assertions.assertTrue(
+ totalFiles.getValue() > baselineTotalFiles,
+ "totalFiles should increase after appending more data");
+ Assertions.assertTrue(
+ totalSize.getValue() > baselineTotalSize,
+ "totalSize should increase after appending more data");
+ }
+
+ @Test
+ public void testPropertyKeyNotFilteredWhenOptimizingDisabled() {
+ // Verify that table-summary.enabled is parsed independently from
self-optimizing.enabled
+ Map<String, String> properties = new HashMap<>();
+ properties.put(TableProperties.ENABLE_SELF_OPTIMIZING, "false");
+ properties.put(TableProperties.TABLE_SUMMARY_ENABLED, "true");
+
+ OptimizingConfig config =
TableConfigurations.parseOptimizingConfig(properties);
+ Assertions.assertFalse(config.isEnabled(), "optimizing should be
disabled");
+ Assertions.assertTrue(
+ config.isTableSummaryEnabled(),
+ "tableSummaryEnabled should be true independently of
optimizing.enabled");
+
+ // Also verify default: when table-summary.enabled is not set, it defaults
to false
+ Map<String, String> defaultProperties = new HashMap<>();
+ defaultProperties.put(TableProperties.ENABLE_SELF_OPTIMIZING, "false");
+
+ OptimizingConfig defaultConfig =
TableConfigurations.parseOptimizingConfig(defaultProperties);
+ Assertions.assertFalse(defaultConfig.isEnabled(), "optimizing should be
disabled");
+ Assertions.assertFalse(
+ defaultConfig.isTableSummaryEnabled(), "tableSummaryEnabled should
default to false");
+ }
+
+ @Test
+ public void testReEnableOptimizingAfterSummaryOnlyMode() {
+ // Setup: add files, switch to summary-only, collect summary
+ initTableWithFiles();
+ UnkeyedTable table = loadUnkeyedTable();
+ table
+ .updateProperties()
+ .set(TableProperties.ENABLE_SELF_OPTIMIZING, "false")
+ .set(TableProperties.TABLE_SUMMARY_ENABLED, "true")
+ .commit();
+ DefaultTableRuntime runtime =
getDefaultTableRuntime(serverTableIdentifier().getId());
+ runtime.refresh(tableService().loadTable(serverTableIdentifier()));
+ refreshPending();
+
+ Map<MetricKey, Metric> metrics =
MetricManager.getInstance().getGlobalRegistry().getMetrics();
+ ServerTableIdentifier identifier = serverTableIdentifier();
+ Gauge<Long> totalFiles = getMetric(metrics, identifier,
TABLE_SUMMARY_TOTAL_FILES);
+ Gauge<Long> healthScore = getMetric(metrics, identifier,
TABLE_SUMMARY_HEALTH_SCORE);
+
+ long summaryOnlyTotalFiles = totalFiles.getValue();
+ Assertions.assertTrue(summaryOnlyTotalFiles > 0, "summary-only mode should
collect metrics");
+
+ // Re-enable optimizing
+ table = loadUnkeyedTable();
+ table.updateProperties().set(TableProperties.ENABLE_SELF_OPTIMIZING,
"true").commit();
+ runtime.refresh(tableService().loadTable(serverTableIdentifier()));
+
+ // Append data and refresh — normal optimizing path should work
+ appendData(loadUnkeyedTable());
+ runtime.refresh(tableService().loadTable(serverTableIdentifier()));
+ refreshPending();
+
+ // Metrics should still be collected via the optimizing-enabled branch
+ Assertions.assertTrue(
+ totalFiles.getValue() > 0, "totalFiles should be collected after
re-enabling optimizing");
+ Assertions.assertTrue(
+ healthScore.getValue() >= 0,
+ "healthScore should be collected after re-enabling optimizing");
+ }
+
+ @Test
+ public void testSummaryCollectedWithOptimizingEnabled() {
+ // Verify that table summary is always collected in the optimizing-enabled
path,
+ // regardless of table-summary.enabled setting
+ initTableWithFiles();
+ refreshPending();
+
+ Map<MetricKey, Metric> metrics =
MetricManager.getInstance().getGlobalRegistry().getMetrics();
+ ServerTableIdentifier identifier = serverTableIdentifier();
+ Gauge<Long> totalFiles = getMetric(metrics, identifier,
TABLE_SUMMARY_TOTAL_FILES);
+ Gauge<Long> totalSize = getMetric(metrics, identifier,
TABLE_SUMMARY_TOTAL_FILES_SIZE);
+ Gauge<Long> healthScore = getMetric(metrics, identifier,
TABLE_SUMMARY_HEALTH_SCORE);
+
+ // With optimizing enabled (default), summary should always be collected
+ Assertions.assertTrue(totalFiles.getValue() > 0, "totalFiles should be >
0");
+ Assertions.assertTrue(totalSize.getValue() > 0, "totalSize should be > 0");
+ Assertions.assertTrue(healthScore.getValue() >= 0, "healthScore should be
>= 0");
+
+ // Append more data and refresh — summary should still be collected
+ long previousTotalFiles = totalFiles.getValue();
+ long previousHealthScore = healthScore.getValue();
+ appendData(loadUnkeyedTable());
+ DefaultTableRuntime runtime =
getDefaultTableRuntime(serverTableIdentifier().getId());
+ runtime.refresh(tableService().loadTable(serverTableIdentifier()));
+ refreshPending();
+
+ Assertions.assertTrue(
+ totalFiles.getValue() >= previousTotalFiles,
+ "totalFiles should be >= previous after refresh with optimizing
enabled");
+ Assertions.assertTrue(
+ healthScore.getValue() >= 0,
+ "healthScore should remain valid after refresh with optimizing
enabled");
+ }
+}
diff --git
a/amoro-common/src/main/java/org/apache/amoro/config/OptimizingConfig.java
b/amoro-common/src/main/java/org/apache/amoro/config/OptimizingConfig.java
index 39afbdbb5..a8efccd3b 100644
--- a/amoro-common/src/main/java/org/apache/amoro/config/OptimizingConfig.java
+++ b/amoro-common/src/main/java/org/apache/amoro/config/OptimizingConfig.java
@@ -29,6 +29,9 @@ public class OptimizingConfig {
// self-optimizing.enabled
private boolean enabled;
+ // table-summary.enabled
+ private boolean tableSummaryEnabled;
+
private boolean allowPartialCommit;
// self-optimizing.quota
@@ -114,6 +117,15 @@ public class OptimizingConfig {
return this;
}
+ public boolean isTableSummaryEnabled() {
+ return tableSummaryEnabled;
+ }
+
+ public OptimizingConfig setTableSummaryEnabled(boolean tableSummaryEnabled) {
+ this.tableSummaryEnabled = tableSummaryEnabled;
+ return this;
+ }
+
public boolean isAllowPartialCommit() {
return allowPartialCommit;
}
@@ -364,6 +376,7 @@ public class OptimizingConfig {
}
OptimizingConfig that = (OptimizingConfig) o;
return enabled == that.enabled
+ && tableSummaryEnabled == that.tableSummaryEnabled
&& allowPartialCommit == that.allowPartialCommit
&& Double.compare(that.targetQuota, targetQuota) == 0
&& maxExecuteRetryCount == that.maxExecuteRetryCount
@@ -396,6 +409,7 @@ public class OptimizingConfig {
public int hashCode() {
return Objects.hashCode(
enabled,
+ tableSummaryEnabled,
allowPartialCommit,
targetQuota,
optimizerGroup,
@@ -427,6 +441,7 @@ public class OptimizingConfig {
public String toString() {
return MoreObjects.toStringHelper(this)
.add("enabled", enabled)
+ .add("tableSummaryEnabled", tableSummaryEnabled)
.add("commitOnPartialSuccess", allowPartialCommit)
.add("targetQuota", targetQuota)
.add("optimizerGroup", optimizerGroup)
diff --git
a/amoro-format-iceberg/src/main/java/org/apache/amoro/table/TableProperties.java
b/amoro-format-iceberg/src/main/java/org/apache/amoro/table/TableProperties.java
index 015c68d48..0905acf42 100644
---
a/amoro-format-iceberg/src/main/java/org/apache/amoro/table/TableProperties.java
+++
b/amoro-format-iceberg/src/main/java/org/apache/amoro/table/TableProperties.java
@@ -76,6 +76,10 @@ public class TableProperties {
public static final boolean ENABLE_SELF_OPTIMIZING_DEFAULT = true;
+ public static final String TABLE_SUMMARY_ENABLED = "table-summary.enabled";
+
+ public static final boolean TABLE_SUMMARY_ENABLED_DEFAULT = false;
+
public static final String SELF_OPTIMIZING_ALLOW_PARTIAL_COMMIT =
"self-optimizing.allow-partial-commit";
public static final boolean SELF_OPTIMIZING_ALLOW_PARTIAL_COMMIT_DEFAULT =
false;