voonhous commented on code in PR #19868:
URL: https://github.com/apache/hudi/pull/19868#discussion_r3961457864


##########
hudi-client/hudi-client-common/src/main/java/org/apache/hudi/metadata/HoodieBackedTableMetadataWriter.java:
##########
@@ -1543,8 +1543,20 @@ public void performTableServices(Option<String> 
inFlightInstantTimestamp, boolea
           .filterCompletedInstants()
           .lastInstant();
       if (!lastInstant.isPresent()) {
+        metrics.ifPresent(m -> m.updateDeltaCommitsSinceLastCompaction(0L));
         return;
       }
+      // Report how far the metadata table has drifted from its last 
compaction, so that a stalled
+      // compaction is visible before the log to base file ratio degrades read 
performance. This is the
+      // backlog as it stands before the compaction below runs, so on a 
healthy table it peaks at
+      // hoodie.metadata.compact.max.delta.commits on the cycle that compacts 
and drops on the next one.
+      // It counts the same completed instants that 
ScheduleCompactionActionExecutor compares against
+      // that threshold, and is not re-sampled afterwards because doing so 
would need another timeline
+      // reload on the write path.
+      metrics.ifPresent(m -> m.updateDeltaCommitsSinceLastCompaction(

Review Comment:
   **major:** This sample sits after 
`runPendingTableServicesOperationsAndRefreshTimeline`, which rethrows (L1623) 
when a pending MDT compaction fails, so in the "compaction keeps failing" state 
this line is never reached again and the gauge freezes at its last value while 
delta commits keep landing. That is silent only with 
`hoodie.metadata.write.fail.on.table.service.failures=false`, but that is the 
deployment relying on a metric. Could we sample in the `finally` block next to 
`TABLE_SERVICE_EXECUTION_DURATION`, from 
`metadataMetaClient.getActiveTimeline()`, so the failure path reports too?
   
   <details><summary>trace</summary>
   
   `compactIfNecessary` -> `writeClient.compact` throws -> L1667 
`COMPACTION_FAILURES++`, rethrow -> compaction instant left pending. Next 
cycle: L1539 -> `filterPendingCompactionTimeline().countInstants() > 0` -> 
`runAnyPendingCompactions()` throws -> L1621 `PENDING_COMPACTIONS_FAILURES++`, 
L1623 rethrow -> outer catch at L1570 -> L1556 skipped. Repeats every cycle 
until the pending compaction succeeds.
   </details>



##########
hudi-client/hudi-client-common/src/main/java/org/apache/hudi/metadata/HoodieBackedTableMetadataWriter.java:
##########
@@ -1543,8 +1543,20 @@ public void performTableServices(Option<String> 
inFlightInstantTimestamp, boolea
           .filterCompletedInstants()
           .lastInstant();
       if (!lastInstant.isPresent()) {
+        metrics.ifPresent(m -> m.updateDeltaCommitsSinceLastCompaction(0L));
         return;
       }
+      // Report how far the metadata table has drifted from its last 
compaction, so that a stalled
+      // compaction is visible before the log to base file ratio degrades read 
performance. This is the
+      // backlog as it stands before the compaction below runs, so on a 
healthy table it peaks at
+      // hoodie.metadata.compact.max.delta.commits on the cycle that compacts 
and drops on the next one.
+      // It counts the same completed instants that 
ScheduleCompactionActionExecutor compares against
+      // that threshold, and is not re-sampled afterwards because doing so 
would need another timeline
+      // reload on the write path.
+      metrics.ifPresent(m -> m.updateDeltaCommitsSinceLastCompaction(
+          
CompactionUtils.getCompletedDeltaCommitsSinceLatestCompaction(activeTimeline)
+              .map(deltaCommitsInfo -> (long) 
deltaCommitsInfo.getLeft().countInstants())
+              .orElse(0L)));

Review Comment:
   **nit:** Feel free to ignore. `.orElse(0L)` is unreachable: L1544 already 
returned unless a completed delta commit exists, and 
`getDeltaCommitsSinceLatestCompaction` returns empty only when the delta-commit 
timeline is empty (`CompactionUtils.java:329-335`). Could we use `.get()` here, 
or keep the fallback with a one-line "defensive" comment?



##########
hudi-common/src/main/java/org/apache/hudi/metadata/HoodieMetadataMetrics.java:
##########
@@ -84,6 +85,22 @@ public class HoodieMetadataMetrics implements Serializable {
   public static final String LOG_COMPACTION_FAILURES = 
"logcompaction_failures";
   public static final String PENDING_COMPACTIONS_FAILURES = 
"pending_compactions_failures";
 
+  // Metadata table compaction health. The existing per-partition 
baseFileCount/logFileCount gauges
+  // show the current shape of the metadata table, but not whether compaction 
is keeping up with it.
+  // Completed delta commits on the metadata table since the last completed 
compaction, sampled before
+  // table services run. On a healthy table this sawtooths up to 
hoodie.metadata.compact.max.delta.commits
+  // and falls back after each compaction, so alert on a multiple of that 
config rather than on the
+  // config value itself. A value that climbs past the peak and keeps going 
means metadata table
+  // compaction is not being scheduled or is failing.
+  public static final String STAT_DELTA_COMMITS_SINCE_LAST_COMPACTION = 
"deltaCommitsSinceLastCompaction";

Review Comment:
   **major:** None of the four new names has a dot, so the CloudWatch reporter 
drops them: `CloudWatchReporter.stageMetricDatum` (c63c9bfa79fd, #19476) skips 
dot-less names, citing open #19507, which names `HoodieMetadataMetrics` as the 
cause. HUDI-9068 (100e9ac47590) prefixed `TABLE_SERVICE_EXECUTION_*` with the 
MDT table name in `performTableServices` for exactly this reason. Could we 
prefix these four the same way (`writeClient.getConfig().getTableName()` there, 
`metaClient.getTableConfig().getTableName()` in `updateSizeMetrics`)?



##########
hudi-common/src/main/java/org/apache/hudi/metadata/HoodieMetadataMetrics.java:
##########
@@ -164,6 +181,48 @@ public void updateSizeMetrics(HoodieTableMetaClient 
metaClient, HoodieBackedTabl
     for (Map.Entry<String, String> e : stats.entrySet()) {
       setMetric(e.getKey(), Long.parseLong(e.getValue()));
     }
+    long totalBaseFiles = sumStat(stats, metadataPartitions, 
STAT_COUNT_BASE_FILES);
+    long totalLogFiles = sumStat(stats, metadataPartitions, 
STAT_COUNT_LOG_FILES);
+    setMetric(STAT_TOTAL_BASE_FILE_COUNT, totalBaseFiles);
+    setMetric(STAT_TOTAL_LOG_FILE_COUNT, totalLogFiles);
+    setMetric(STAT_LOG_TO_BASE_FILE_RATIO_PERCENT, 
logToBaseFileRatioPercent(totalBaseFiles, totalLogFiles));

Review Comment:
   **major:** `updateSizeMetrics` has no test anywhere (`grep -rn 
updateSizeMetrics` hits only two main-source lines), and the three new gauges 
are asserted only as pure helpers, so swapping the two `setMetric` arguments on 
L186-187 passes every test. Both existing `testMetadataMetrics` 
(`TestHoodieBackedMetadata.java:3812-3815`, 
`TestJavaHoodieBackedMetadata.java:2616-2623`) already assert the per-partition 
gauges from this same call. Could we add `containsKey` for the four new names 
there, rather than in a new file?



##########
hudi-common/src/main/java/org/apache/hudi/metadata/HoodieMetadataMetrics.java:
##########
@@ -164,6 +181,48 @@ public void updateSizeMetrics(HoodieTableMetaClient 
metaClient, HoodieBackedTabl
     for (Map.Entry<String, String> e : stats.entrySet()) {
       setMetric(e.getKey(), Long.parseLong(e.getValue()));
     }
+    long totalBaseFiles = sumStat(stats, metadataPartitions, 
STAT_COUNT_BASE_FILES);
+    long totalLogFiles = sumStat(stats, metadataPartitions, 
STAT_COUNT_LOG_FILES);
+    setMetric(STAT_TOTAL_BASE_FILE_COUNT, totalBaseFiles);
+    setMetric(STAT_TOTAL_LOG_FILE_COUNT, totalLogFiles);
+    setMetric(STAT_LOG_TO_BASE_FILE_RATIO_PERCENT, 
logToBaseFileRatioPercent(totalBaseFiles, totalLogFiles));
+  }
+
+  /**
+   * Sums a per-partition stat, keyed as {@code <partition>.<statName>}, over 
the enabled metadata partitions.
+   */
+  @VisibleForTesting
+  static long sumStat(Map<String, String> stats, Set<String> 
metadataPartitions, String statName) {
+    return metadataPartitions.stream()
+        .mapToLong(partition -> Long.parseLong(stats.getOrDefault(partition + 
"." + statName, "0")))
+        .sum();
+  }
+
+  /**
+   * Log files per base file expressed as a percentage, so that it can be 
reported through a long valued gauge.
+   *
+   * <p>Returns 0 when there is no base file to divide by. That case is not 
necessarily healthy: a metadata
+   * table holding log files but no base file has never been compacted. Read 
this gauge together with
+   * {@link #STAT_TOTAL_BASE_FILE_COUNT} and {@link 
#STAT_TOTAL_LOG_FILE_COUNT} rather than on its own.
+   */
+  @VisibleForTesting
+  static long logToBaseFileRatioPercent(long totalBaseFiles, long 
totalLogFiles) {
+    return totalBaseFiles > 0 ? (totalLogFiles * 100L) / totalBaseFiles : 0L;
+  }
+
+  /**
+   * Reports the number of completed metadata table delta commits since the 
last completed compaction.
+   *
+   * <p>This counts the same instants that {@code 
ScheduleCompactionActionExecutor#needCompact} compares
+   * against the metadata table's delta commit threshold, which is configured 
by
+   * {@code hoodie.metadata.compact.max.delta.commits} (default 10) under the 
default {@code NUM_COMMITS}
+   * trigger strategy. Compaction fires once the count reaches that threshold, 
and the gauge is sampled
+   * before table services run, so on a healthy table it peaks at the 
threshold on the cycle where
+   * compaction fires and drops on the next one. Alert on a multiple of the 
threshold; a stalled

Review Comment:
   **major:** "climbs past it without bound" does not hold for a 
never-scheduled stall. 
`CompactionUtils.getEarliestInstantToRetainForCompaction` (L451-457) pins the 
last compaction only while `numDeltaCommits < max`; at 10 or more the pin moves 
to a delta commit, MDT archival (`keep.min+1 / keep.max+1` = 21/31, 
`HoodieMetadataWriteUtils.java:293-296`) drops the compaction, and the helper's 
"no compaction" branch saturates the gauge at 21-31, indistinguishable from 
never-compacted. An alert at 3x-4x never fires. Could we reword this and the PR 
body to say it saturates near `hoodie.keep.min.commits`, and alert at about 2x 
the threshold?
   
   <details><summary>why the other stall modes differ</summary>
   
   A TSM-delegated stall leaves the compaction instant pending, which 
`TimelineArchiverV2` candidate #1 (earliest pending instant) pins, so that one 
does climb. A failing compaction never reaches the sample at all (see 
`HoodieBackedTableMetadataWriter.java:1556`). Only the never-scheduled case 
(e.g. `validateCompactionScheduling` false behind a long-running DT instant) 
hits the plateau; MDT candidate #5 pins at the DT's earliest active instant, 
which sits after the last MDT compaction, so it does not retain it either.
   </details>



##########
hudi-common/src/main/java/org/apache/hudi/metadata/HoodieMetadataMetrics.java:
##########
@@ -164,6 +181,48 @@ public void updateSizeMetrics(HoodieTableMetaClient 
metaClient, HoodieBackedTabl
     for (Map.Entry<String, String> e : stats.entrySet()) {
       setMetric(e.getKey(), Long.parseLong(e.getValue()));
     }
+    long totalBaseFiles = sumStat(stats, metadataPartitions, 
STAT_COUNT_BASE_FILES);
+    long totalLogFiles = sumStat(stats, metadataPartitions, 
STAT_COUNT_LOG_FILES);
+    setMetric(STAT_TOTAL_BASE_FILE_COUNT, totalBaseFiles);
+    setMetric(STAT_TOTAL_LOG_FILE_COUNT, totalLogFiles);
+    setMetric(STAT_LOG_TO_BASE_FILE_RATIO_PERCENT, 
logToBaseFileRatioPercent(totalBaseFiles, totalLogFiles));
+  }
+
+  /**
+   * Sums a per-partition stat, keyed as {@code <partition>.<statName>}, over 
the enabled metadata partitions.
+   */
+  @VisibleForTesting
+  static long sumStat(Map<String, String> stats, Set<String> 
metadataPartitions, String statName) {
+    return metadataPartitions.stream()
+        .mapToLong(partition -> Long.parseLong(stats.getOrDefault(partition + 
"." + statName, "0")))
+        .sum();
+  }
+
+  /**
+   * Log files per base file expressed as a percentage, so that it can be 
reported through a long valued gauge.
+   *
+   * <p>Returns 0 when there is no base file to divide by. That case is not 
necessarily healthy: a metadata
+   * table holding log files but no base file has never been compacted. Read 
this gauge together with
+   * {@link #STAT_TOTAL_BASE_FILE_COUNT} and {@link 
#STAT_TOTAL_LOG_FILE_COUNT} rather than on its own.
+   */
+  @VisibleForTesting
+  static long logToBaseFileRatioPercent(long totalBaseFiles, long 
totalLogFiles) {
+    return totalBaseFiles > 0 ? (totalLogFiles * 100L) / totalBaseFiles : 0L;

Review Comment:
   **minor:** Not blocking. This ratio is file-group-weighted: `getStats` 
counts per latest file slice, `files` has one file group and `record_index` 
defaults to 10 (global: 10000), so RLI drowns the partition that actually hurts 
listings. Worked case: `files` 1 base + 20 logs, RLI 1000 + 1000 reports 101%, 
"about one log per base", while every partition listing merges 20 log files. 
Since `getStats` already has the per-partition ints, could we emit the max 
ratio across partitions instead of the sum ratio?



##########
hudi-common/src/main/java/org/apache/hudi/metadata/HoodieMetadataMetrics.java:
##########
@@ -164,6 +181,48 @@ public void updateSizeMetrics(HoodieTableMetaClient 
metaClient, HoodieBackedTabl
     for (Map.Entry<String, String> e : stats.entrySet()) {
       setMetric(e.getKey(), Long.parseLong(e.getValue()));
     }
+    long totalBaseFiles = sumStat(stats, metadataPartitions, 
STAT_COUNT_BASE_FILES);
+    long totalLogFiles = sumStat(stats, metadataPartitions, 
STAT_COUNT_LOG_FILES);
+    setMetric(STAT_TOTAL_BASE_FILE_COUNT, totalBaseFiles);
+    setMetric(STAT_TOTAL_LOG_FILE_COUNT, totalLogFiles);
+    setMetric(STAT_LOG_TO_BASE_FILE_RATIO_PERCENT, 
logToBaseFileRatioPercent(totalBaseFiles, totalLogFiles));
+  }
+
+  /**
+   * Sums a per-partition stat, keyed as {@code <partition>.<statName>}, over 
the enabled metadata partitions.
+   */
+  @VisibleForTesting
+  static long sumStat(Map<String, String> stats, Set<String> 
metadataPartitions, String statName) {
+    return metadataPartitions.stream()
+        .mapToLong(partition -> Long.parseLong(stats.getOrDefault(partition + 
"." + statName, "0")))

Review Comment:
   **minor:** Not blocking. `getOrDefault(..., "0")` is unreachable: `getStats` 
writes all four keys for every member of the same `metadataPartitions` set 
(L132-158), so `sumStat` re-parses strings it just produced, and 
`missingPartitionStatsAreTreatedAsZero` plus the `record_index` half of 
`statsAreSummedAcrossEnabledPartitionsOnly` assert states production cannot 
produce. Could we sum the two ints inside `getStats` and drop `sumStat` and 
those two tests?



##########
hudi-common/src/main/java/org/apache/hudi/metadata/HoodieMetadataMetrics.java:
##########
@@ -84,6 +85,22 @@ public class HoodieMetadataMetrics implements Serializable {
   public static final String LOG_COMPACTION_FAILURES = 
"logcompaction_failures";
   public static final String PENDING_COMPACTIONS_FAILURES = 
"pending_compactions_failures";
 
+  // Metadata table compaction health. The existing per-partition 
baseFileCount/logFileCount gauges
+  // show the current shape of the metadata table, but not whether compaction 
is keeping up with it.
+  // Completed delta commits on the metadata table since the last completed 
compaction, sampled before
+  // table services run. On a healthy table this sawtooths up to 
hoodie.metadata.compact.max.delta.commits
+  // and falls back after each compaction, so alert on a multiple of that 
config rather than on the
+  // config value itself. A value that climbs past the peak and keeps going 
means metadata table
+  // compaction is not being scheduled or is failing.

Review Comment:
   **minor:** Not blocking. With `hoodie.metadata.streaming.write.enabled`, 
`performTableServices` is never called 
(`FlinkHoodieBackedTableMetadataWriter.java:145` and 
`HoodieFlinkTableServiceClient.java:209` both guard on 
`!isStreamingWriteEnabled()`), so this gauge is never registered on the 
deployment where MDT compaction runs in a separate pipeline and can stall 
unnoticed. Could we note that gap in this comment, or emit it from the Flink 
compaction pipeline as well?



##########
hudi-common/src/main/java/org/apache/hudi/metadata/HoodieMetadataMetrics.java:
##########
@@ -84,6 +85,22 @@ public class HoodieMetadataMetrics implements Serializable {
   public static final String LOG_COMPACTION_FAILURES = 
"logcompaction_failures";
   public static final String PENDING_COMPACTIONS_FAILURES = 
"pending_compactions_failures";
 
+  // Metadata table compaction health. The existing per-partition 
baseFileCount/logFileCount gauges

Review Comment:
   **nit:** Feel free to ignore. `STAT_LAST_COMPACTION_TIMESTAMP` (L79) has 
been declared and never emitted since 298808baaf77 (2020-12-30). Since this 
block adds a compaction-health gauge next to it, would it be worth either 
wiring it up (an age-since-compaction gauge would not freeze on the failure 
path noted at `HoodieBackedTableMetadataWriter.java:1556`) or deleting it?



##########
hudi-client/hudi-client-common/src/test/java/org/apache/hudi/metadata/TestHoodieBackedTableMetadataWriter.java:
##########
@@ -651,4 +651,83 @@ void testPerformTableServicesWithFailureHandling(
     // Verify metrics are incremented when there's a failure
     verify(metrics, 
times(1)).incrementMetric(HoodieMetadataMetrics.PENDING_COMPACTIONS_FAILURES, 
1);
   }
+
+  @Test
+  void performTableServicesReportsCompletedDeltaCommitsSinceLastCompaction() 
throws Exception {
+    // A completed compaction, then three completed delta commits after it, 
plus one still inflight.
+    // The gauge must count the three completed instants only, so that it 
stays comparable to
+    // hoodie.compact.inline.max.delta.commits, which the compaction trigger 
evaluates the same way.
+    List<HoodieInstant> instants = new ArrayList<>();
+    instants.add(INSTANT_GENERATOR.createNewInstant(
+        HoodieInstant.State.COMPLETED, HoodieTimeline.DELTA_COMMIT_ACTION, 
"001", "0011"));
+    instants.add(INSTANT_GENERATOR.createNewInstant(
+        HoodieInstant.State.COMPLETED, HoodieTimeline.COMMIT_ACTION, "002", 
"0021"));
+    instants.add(INSTANT_GENERATOR.createNewInstant(
+        HoodieInstant.State.COMPLETED, HoodieTimeline.DELTA_COMMIT_ACTION, 
"003", "0031"));
+    instants.add(INSTANT_GENERATOR.createNewInstant(
+        HoodieInstant.State.COMPLETED, HoodieTimeline.DELTA_COMMIT_ACTION, 
"004", "0041"));
+    instants.add(INSTANT_GENERATOR.createNewInstant(
+        HoodieInstant.State.COMPLETED, HoodieTimeline.DELTA_COMMIT_ACTION, 
"005", "0051"));
+    instants.add(INSTANT_GENERATOR.createNewInstant(
+        HoodieInstant.State.INFLIGHT, HoodieTimeline.DELTA_COMMIT_ACTION, 
"006"));
+
+    HoodieMetadataMetrics metrics = mock(HoodieMetadataMetrics.class);
+    writerForTableServices(metrics, 
createMockTimeline(instants)).performTableServices(Option.empty(), true);
+
+    verify(metrics, times(1)).updateDeltaCommitsSinceLastCompaction(3L);

Review Comment:
   **minor:** Not blocking. Every delta commit here is requested and completed 
on the same side of the compaction's requested time, so this passes identically 
under `findInstantsAfter(requestedTime)`; the completion-time semantics 
HUDI-2461 (61f35ebe423d) introduced are unpinned. No case reaches 0 through the 
main branch either. Could we add `createNewInstant(COMPLETED, 
DELTA_COMMIT_ACTION, "0015", "0035")` (requested before "002", completed after) 
expecting 4, and a compaction-last case expecting 0?



##########
hudi-client/hudi-client-common/src/test/java/org/apache/hudi/metadata/TestHoodieBackedTableMetadataWriter.java:
##########
@@ -651,4 +651,83 @@ void testPerformTableServicesWithFailureHandling(
     // Verify metrics are incremented when there's a failure
     verify(metrics, 
times(1)).incrementMetric(HoodieMetadataMetrics.PENDING_COMPACTIONS_FAILURES, 
1);
   }
+
+  @Test
+  void performTableServicesReportsCompletedDeltaCommitsSinceLastCompaction() 
throws Exception {
+    // A completed compaction, then three completed delta commits after it, 
plus one still inflight.
+    // The gauge must count the three completed instants only, so that it 
stays comparable to
+    // hoodie.compact.inline.max.delta.commits, which the compaction trigger 
evaluates the same way.
+    List<HoodieInstant> instants = new ArrayList<>();
+    instants.add(INSTANT_GENERATOR.createNewInstant(
+        HoodieInstant.State.COMPLETED, HoodieTimeline.DELTA_COMMIT_ACTION, 
"001", "0011"));
+    instants.add(INSTANT_GENERATOR.createNewInstant(
+        HoodieInstant.State.COMPLETED, HoodieTimeline.COMMIT_ACTION, "002", 
"0021"));
+    instants.add(INSTANT_GENERATOR.createNewInstant(
+        HoodieInstant.State.COMPLETED, HoodieTimeline.DELTA_COMMIT_ACTION, 
"003", "0031"));
+    instants.add(INSTANT_GENERATOR.createNewInstant(
+        HoodieInstant.State.COMPLETED, HoodieTimeline.DELTA_COMMIT_ACTION, 
"004", "0041"));
+    instants.add(INSTANT_GENERATOR.createNewInstant(
+        HoodieInstant.State.COMPLETED, HoodieTimeline.DELTA_COMMIT_ACTION, 
"005", "0051"));
+    instants.add(INSTANT_GENERATOR.createNewInstant(
+        HoodieInstant.State.INFLIGHT, HoodieTimeline.DELTA_COMMIT_ACTION, 
"006"));
+
+    HoodieMetadataMetrics metrics = mock(HoodieMetadataMetrics.class);
+    writerForTableServices(metrics, 
createMockTimeline(instants)).performTableServices(Option.empty(), true);
+
+    verify(metrics, times(1)).updateDeltaCommitsSinceLastCompaction(3L);
+  }
+
+  @Test
+  void performTableServicesReportsAllDeltaCommitsWhenNeverCompacted() throws 
Exception {

Review Comment:
   **minor:** Not blocking. This scenario is already asserted 
instant-by-instant in 
`TestCompactionUtils.testGetDeltaCommitsSinceLatestCompaction` 
(`hudi-hadoop-common/.../TestCompactionUtils.java:258`, `prepareTimeline` 
L692-705), and it adds no wiring coverage over the sibling above. Could we fold 
the three `performTableServicesReports*` tests into one `@ParameterizedTest` 
over `(instants, expectedCount)`, as this file does at L135 and L532, dropping 
this row?



##########
hudi-client/hudi-client-common/src/test/java/org/apache/hudi/metadata/TestHoodieBackedTableMetadataWriter.java:
##########
@@ -651,4 +651,83 @@ void testPerformTableServicesWithFailureHandling(
     // Verify metrics are incremented when there's a failure
     verify(metrics, 
times(1)).incrementMetric(HoodieMetadataMetrics.PENDING_COMPACTIONS_FAILURES, 
1);
   }
+
+  @Test
+  void performTableServicesReportsCompletedDeltaCommitsSinceLastCompaction() 
throws Exception {
+    // A completed compaction, then three completed delta commits after it, 
plus one still inflight.
+    // The gauge must count the three completed instants only, so that it 
stays comparable to
+    // hoodie.compact.inline.max.delta.commits, which the compaction trigger 
evaluates the same way.
+    List<HoodieInstant> instants = new ArrayList<>();
+    instants.add(INSTANT_GENERATOR.createNewInstant(
+        HoodieInstant.State.COMPLETED, HoodieTimeline.DELTA_COMMIT_ACTION, 
"001", "0011"));
+    instants.add(INSTANT_GENERATOR.createNewInstant(
+        HoodieInstant.State.COMPLETED, HoodieTimeline.COMMIT_ACTION, "002", 
"0021"));
+    instants.add(INSTANT_GENERATOR.createNewInstant(
+        HoodieInstant.State.COMPLETED, HoodieTimeline.DELTA_COMMIT_ACTION, 
"003", "0031"));
+    instants.add(INSTANT_GENERATOR.createNewInstant(
+        HoodieInstant.State.COMPLETED, HoodieTimeline.DELTA_COMMIT_ACTION, 
"004", "0041"));
+    instants.add(INSTANT_GENERATOR.createNewInstant(
+        HoodieInstant.State.COMPLETED, HoodieTimeline.DELTA_COMMIT_ACTION, 
"005", "0051"));
+    instants.add(INSTANT_GENERATOR.createNewInstant(
+        HoodieInstant.State.INFLIGHT, HoodieTimeline.DELTA_COMMIT_ACTION, 
"006"));
+
+    HoodieMetadataMetrics metrics = mock(HoodieMetadataMetrics.class);
+    writerForTableServices(metrics, 
createMockTimeline(instants)).performTableServices(Option.empty(), true);
+
+    verify(metrics, times(1)).updateDeltaCommitsSinceLastCompaction(3L);
+  }
+
+  @Test
+  void performTableServicesReportsAllDeltaCommitsWhenNeverCompacted() throws 
Exception {
+    // No compaction has ever run, so every completed delta commit counts as 
backlog.
+    List<HoodieInstant> instants = new ArrayList<>();
+    instants.add(INSTANT_GENERATOR.createNewInstant(
+        HoodieInstant.State.COMPLETED, HoodieTimeline.DELTA_COMMIT_ACTION, 
"001", "0011"));
+    instants.add(INSTANT_GENERATOR.createNewInstant(
+        HoodieInstant.State.COMPLETED, HoodieTimeline.DELTA_COMMIT_ACTION, 
"002", "0021"));
+
+    HoodieMetadataMetrics metrics = mock(HoodieMetadataMetrics.class);
+    writerForTableServices(metrics, 
createMockTimeline(instants)).performTableServices(Option.empty(), true);
+
+    verify(metrics, times(1)).updateDeltaCommitsSinceLastCompaction(2L);
+  }
+
+  @Test
+  void performTableServicesReportsZeroDeltaCommitsOnEmptyTimeline() throws 
Exception {
+    HoodieMetadataMetrics metrics = mock(HoodieMetadataMetrics.class);
+    writerForTableServices(metrics, createMockTimeline(new ArrayList<>()))
+        .performTableServices(Option.empty(), true);
+
+    // No completed delta commit yet, so the gauge is reset rather than left 
at a stale value.
+    verify(metrics, times(1)).updateDeltaCommitsSinceLastCompaction(0L);
+  }
+
+  /**
+   * Builds a partially mocked writer whose {@code performTableServices} runs 
for real against the given
+   * metadata timeline.
+   */
+  private static HoodieBackedTableMetadataWriter 
writerForTableServices(HoodieMetadataMetrics metrics,

Review Comment:
   **minor:** Not blocking. This is the same setup as 
`testPerformTableServicesWithFailureHandling` (L573-640): four mocks, the same 
six stubs, the same four field injections and `doCallRealMethod` on 
`performTableServices`, except the older test still hand-rolls reflection 
instead of `setField` (L518). Could that test be switched onto this helper so 
there is one setup path?



##########
hudi-client/hudi-client-common/src/test/java/org/apache/hudi/metadata/TestHoodieBackedTableMetadataWriter.java:
##########
@@ -523,7 +523,7 @@ private static void setField(Object target, String name, 
Object value) throws Ex
   }
 
   @SuppressWarnings("deprecation")
-  private HoodieActiveTimeline createMockTimeline(List<HoodieInstant> 
instants) {
+  private static HoodieActiveTimeline createMockTimeline(List<HoodieInstant> 
instants) {

Review Comment:
   **nit:** Feel free to ignore. All four callers of `createMockTimeline` are 
instance `@Test` methods and `writerForTableServices` never calls it, so the 
`static` here is unneeded churn. Could we revert this line?
   
   ```suggestion
     private HoodieActiveTimeline createMockTimeline(List<HoodieInstant> 
instants) {
   ```



##########
hudi-client/hudi-client-common/src/test/java/org/apache/hudi/metadata/TestHoodieBackedTableMetadataWriter.java:
##########
@@ -651,4 +651,83 @@ void testPerformTableServicesWithFailureHandling(
     // Verify metrics are incremented when there's a failure
     verify(metrics, 
times(1)).incrementMetric(HoodieMetadataMetrics.PENDING_COMPACTIONS_FAILURES, 
1);
   }
+
+  @Test
+  void performTableServicesReportsCompletedDeltaCommitsSinceLastCompaction() 
throws Exception {
+    // A completed compaction, then three completed delta commits after it, 
plus one still inflight.
+    // The gauge must count the three completed instants only, so that it 
stays comparable to
+    // hoodie.compact.inline.max.delta.commits, which the compaction trigger 
evaluates the same way.

Review Comment:
   **nit:** Feel free to ignore. This names 
`hoodie.compact.inline.max.delta.commits`, while the javadoc on 
`updateDeltaCommitsSinceLastCompaction` names 
`hoodie.metadata.compact.max.delta.commits` (`HoodieMetadataConfig.java:141`, 
mapped onto the inline knob for the MDT write config at 
`HoodieMetadataWriteUtils.java:301`). Could we use the metadata key here too?
   
   ```suggestion
       // hoodie.metadata.compact.max.delta.commits, which the compaction 
trigger evaluates the same way.
   ```



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