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

ahmedabu98 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/beam.git


The following commit(s) were added to refs/heads/master by this push:
     new 33547f6ccad more robust metrics (#40394)
33547f6ccad is described below

commit 33547f6ccad486a819e895fe4a91fbbd93666856
Author: Ahmed Abualsaud <[email protected]>
AuthorDate: Sun Oct 11 21:38:59 2026 -0700

    more robust metrics (#40394)
---
 .../sdk/io/iceberg/cdc/sink/CommitterMetrics.java  | 73 ++++++++--------------
 .../beam/sdk/io/iceberg/cdc/sink/package-info.java | 10 +--
 .../sdk/io/iceberg/cdc/sink/CommitDeltasTest.java  | 34 +++++++---
 .../io/iceberg/cdc/sink/CommitterMetricsTest.java  | 27 ++++----
 4 files changed, 70 insertions(+), 74 deletions(-)

diff --git 
a/sdks/java/io/iceberg/src/main/java/org/apache/beam/sdk/io/iceberg/cdc/sink/CommitterMetrics.java
 
b/sdks/java/io/iceberg/src/main/java/org/apache/beam/sdk/io/iceberg/cdc/sink/CommitterMetrics.java
index 63d2a13c701..c7330386f5d 100644
--- 
a/sdks/java/io/iceberg/src/main/java/org/apache/beam/sdk/io/iceberg/cdc/sink/CommitterMetrics.java
+++ 
b/sdks/java/io/iceberg/src/main/java/org/apache/beam/sdk/io/iceberg/cdc/sink/CommitterMetrics.java
@@ -33,12 +33,14 @@ import org.checkerframework.checker.nullness.qual.Nullable;
 final class CommitterMetrics implements Serializable {
 
   final Counter snapshotsCreated = Metrics.counter(CommitDeltas.class, 
"snapshotsCreated");
-  final Counter committedDataFiles = Metrics.counter(CommitDeltas.class, 
"committedDataFiles");
-  final Counter committedDeleteFiles = Metrics.counter(CommitDeltas.class, 
"committedDeleteFiles");
-  final Counter committedRecords = Metrics.counter(CommitDeltas.class, 
"committedRecords");
-  final Counter committedEqualityDeleteRecords =
-      Metrics.counter(CommitDeltas.class, "committedEqualityDeleteRecords");
-  final Counter committedBytes = Metrics.counter(CommitDeltas.class, 
"committedBytes");
+  final Distribution committedDataFileRecordCount =
+      Metrics.distribution(CommitDeltas.class, "committedDataFileRecordCount");
+  final Distribution committedDataFileByteSize =
+      Metrics.distribution(CommitDeltas.class, "committedDataFileByteSize");
+  final Distribution committedEqualityDeleteRecordCount =
+      Metrics.distribution(CommitDeltas.class, 
"committedEqualityDeleteRecordCount");
+  final Distribution committedEqualityDeleteByteSize =
+      Metrics.distribution(CommitDeltas.class, 
"committedEqualityDeleteByteSize");
   final Distribution commitDurationMs =
       Metrics.distribution(CommitDeltas.class, "commitDurationMs");
   final Counter commitFailures = Metrics.counter(CommitDeltas.class, 
"commitFailures");
@@ -57,79 +59,58 @@ final class CommitterMetrics implements Serializable {
 
   /** Records a successful commit's volume metrics. */
   void recordCommit(CommitSummary summary) {
-    committedDataFiles.inc(summary.dataFileCount);
-    committedDeleteFiles.inc(summary.deleteFileCount);
-    committedRecords.inc(summary.dataRecords);
-    committedEqualityDeleteRecords.inc(summary.equalityDeleteRecords);
-    committedBytes.inc(summary.bytes);
+    for (DataFile f : summary.dataFiles) {
+      committedDataFileRecordCount.update(f.recordCount());
+      committedDataFileByteSize.update(f.fileSizeInBytes());
+    }
+    for (DeleteFile f : summary.deleteFiles) {
+      if (f.content() == FileContent.EQUALITY_DELETES) {
+        committedEqualityDeleteRecordCount.update(f.recordCount());
+        committedEqualityDeleteByteSize.update(f.fileSizeInBytes());
+      }
+    }
     snapshotsCreated.inc();
   }
 
-  /** One commit's volume and partition-spec summary, computed from its files. 
*/
+  /** One commit's files and partition-spec summary. */
   static final class CommitSummary {
-    final long dataFileCount;
-    final long deleteFileCount;
-    final long dataRecords;
-    final long equalityDeleteRecords;
-    final long bytes;
+    final List<DataFile> dataFiles;
+    final List<DeleteFile> deleteFiles;
     final @Nullable Integer firstSpecId;
     final Set<Integer> specIds;
     final boolean hasEqualityDeletes;
 
     private CommitSummary(
-        long dataFileCount,
-        long deleteFileCount,
-        long dataRecords,
-        long equalityDeleteRecords,
-        long bytes,
+        List<DataFile> dataFiles,
+        List<DeleteFile> deleteFiles,
         @Nullable Integer firstSpecId,
         Set<Integer> specIds,
         boolean hasEqualityDeletes) {
-      this.dataFileCount = dataFileCount;
-      this.deleteFileCount = deleteFileCount;
-      this.dataRecords = dataRecords;
-      this.equalityDeleteRecords = equalityDeleteRecords;
-      this.bytes = bytes;
+      this.dataFiles = dataFiles;
+      this.deleteFiles = deleteFiles;
       this.firstSpecId = firstSpecId;
       this.specIds = specIds;
       this.hasEqualityDeletes = hasEqualityDeletes;
     }
 
     static CommitSummary of(List<DataFile> dataFiles, List<DeleteFile> 
deleteFiles) {
-      long dataRecords = 0;
-      long bytes = 0;
       @Nullable Integer firstSpecId = null;
       Set<Integer> specIds = new HashSet<>();
       for (DataFile f : dataFiles) {
-        dataRecords += f.recordCount();
-        bytes += f.fileSizeInBytes();
         if (firstSpecId == null) {
           firstSpecId = f.specId();
         }
         specIds.add(f.specId());
       }
-      long equalityDeleteRecords = 0;
       boolean hasEqualityDeletes = false;
       for (DeleteFile f : deleteFiles) {
-        bytes += f.fileSizeInBytes();
         if (firstSpecId == null) {
           firstSpecId = f.specId();
         }
         specIds.add(f.specId());
-        if (f.content() == FileContent.EQUALITY_DELETES) {
-          equalityDeleteRecords += f.recordCount();
-          hasEqualityDeletes = true;
-        }
+        hasEqualityDeletes |= f.content() == FileContent.EQUALITY_DELETES;
       }
-      return new CommitSummary(
-          dataFiles.size(),
-          deleteFiles.size(),
-          dataRecords,
-          equalityDeleteRecords,
-          bytes,
-          firstSpecId,
-          specIds,
-          hasEqualityDeletes);
+      return new CommitSummary(dataFiles, deleteFiles, firstSpecId, specIds, 
hasEqualityDeletes);
     }
   }
 }
diff --git 
a/sdks/java/io/iceberg/src/main/java/org/apache/beam/sdk/io/iceberg/cdc/sink/package-info.java
 
b/sdks/java/io/iceberg/src/main/java/org/apache/beam/sdk/io/iceberg/cdc/sink/package-info.java
index a31833a615e..1793afd3d56 100644
--- 
a/sdks/java/io/iceberg/src/main/java/org/apache/beam/sdk/io/iceberg/cdc/sink/package-info.java
+++ 
b/sdks/java/io/iceberg/src/main/java/org/apache/beam/sdk/io/iceberg/cdc/sink/package-info.java
@@ -41,12 +41,12 @@
  *   <li><b>snapshotsCreated</b>: CDC snapshots committed. In streaming this 
should track one per
  *       commit window per destination. A flat line while data is flowing 
means commits have
  *       stalled.
- *   <li><b>committedDataFiles</b> / <b>committedDeleteFiles</b>: files 
published. Divide by {@code
- *       snapshotsCreated} for files per commit. A large ratio points to the 
small-file problem
+ *   <li><b>committedDataFileRecordCount</b> / 
<b>committedEqualityDeleteRecordCount</b>
+ *       (distributions, per file): rows in each committed data file and 
equality-delete file. Their
+ *       counts are the files committed; divide by {@code snapshotsCreated} 
for files per commit.
+ *   <li><b>committedDataFileByteSize</b> / 
<b>committedEqualityDeleteByteSize</b> (distributions,
+ *       per file): size of each committed file. A low mean points to the 
small-file problem
  *       described below.
- *   <li><b>committedRecords</b>: rows committed.
- *   <li><b>committedEqualityDeleteRecords</b>: equality-delete rows committed.
- *   <li><b>committedBytes</b>: total bytes of committed files.
  *   <li><b>commitDurationMs</b> (distribution): wall-clock time spent waiting 
on the Iceberg commit
  *       operation. Its tail is your catalog's health: a growing maximum 
usually means catalog
  *       contention, not a Beam problem.
diff --git 
a/sdks/java/io/iceberg/src/test/java/org/apache/beam/sdk/io/iceberg/cdc/sink/CommitDeltasTest.java
 
b/sdks/java/io/iceberg/src/test/java/org/apache/beam/sdk/io/iceberg/cdc/sink/CommitDeltasTest.java
index 95dd70de328..38b38ca1b4d 100644
--- 
a/sdks/java/io/iceberg/src/test/java/org/apache/beam/sdk/io/iceberg/cdc/sink/CommitDeltasTest.java
+++ 
b/sdks/java/io/iceberg/src/test/java/org/apache/beam/sdk/io/iceberg/cdc/sink/CommitDeltasTest.java
@@ -56,6 +56,7 @@ import org.apache.beam.sdk.io.iceberg.IcebergCatalogConfig;
 import org.apache.beam.sdk.io.iceberg.SerializableDataFile;
 import org.apache.beam.sdk.io.iceberg.SerializableDeleteFile;
 import org.apache.beam.sdk.io.iceberg.SnapshotInfo;
+import org.apache.beam.sdk.metrics.DistributionResult;
 import org.apache.beam.sdk.metrics.MetricNameFilter;
 import org.apache.beam.sdk.metrics.MetricResult;
 import org.apache.beam.sdk.metrics.Metrics;
@@ -302,6 +303,19 @@ public class CommitDeltasTest {
     return total;
   }
 
+  /** The named {@link CommitDeltas} distribution committed by the pipeline. */
+  private static DistributionResult distribution(PipelineResult result, String 
name) {
+    return Iterables.getOnlyElement(
+            result
+                .metrics()
+                .queryMetrics(
+                    MetricsFilter.builder()
+                        
.addNameFilter(MetricNameFilter.named(CommitDeltas.class, name))
+                        .build())
+                .getDistributions())
+        .getCommitted();
+  }
+
   /**
    * Seeds a real prior CDC snapshot for {@code sinkId}, token keys written as 
the committer would.
    * The keys are literal strings on purpose: they double as a pin of the 
on-disk contract.
@@ -1243,7 +1257,7 @@ public class CommitDeltasTest {
   // Commit volume metrics.
   // 
---------------------------------------------------------------------------------------------
 
-  /** A commit records its volume metrics: file/record counts, bytes, 
snapshots created. */
+  /** A commit records its volume metrics: records and bytes per committed 
file. */
   @Test
   public void recordsCommitVolumeMetrics() {
     TestTable tt = v2Table();
@@ -1283,12 +1297,18 @@ public class CommitDeltasTest {
     result.waitUntilFinish();
 
     assertThat(counter(result, "snapshotsCreated"), equalTo(1L));
-    assertThat(counter(result, "committedDataFiles"), equalTo(2L));
-    assertThat(counter(result, "committedDeleteFiles"), equalTo(2L));
-    // 2 data files carry 1 record each (id=1, id=2).
-    assertThat(counter(result, "committedRecords"), equalTo(2L));
-    assertThat(counter(result, "committedEqualityDeleteRecords"), equalTo(2L));
-    assertThat(counter(result, "committedBytes"), greaterThan(0L));
+    // One commit with 2 data files and 2 equality-delete files, each carrying 
1 record (rows
+    // id=1, id=2; deletes for id=1, id=3).
+    assertThat(
+        distribution(result, "committedDataFileRecordCount"),
+        equalTo(DistributionResult.create(2, 2, 1, 1)));
+    assertThat(
+        distribution(result, "committedEqualityDeleteRecordCount"),
+        equalTo(DistributionResult.create(2, 2, 1, 1)));
+    for (String bytes : List.of("committedDataFileByteSize", 
"committedEqualityDeleteByteSize")) {
+      assertThat(distribution(result, bytes).getCount(), equalTo(2L));
+      assertThat(distribution(result, bytes).getMin(), greaterThan(0L));
+    }
   }
 
   // 
---------------------------------------------------------------------------------------------
diff --git 
a/sdks/java/io/iceberg/src/test/java/org/apache/beam/sdk/io/iceberg/cdc/sink/CommitterMetricsTest.java
 
b/sdks/java/io/iceberg/src/test/java/org/apache/beam/sdk/io/iceberg/cdc/sink/CommitterMetricsTest.java
index 9f8f73f7319..ce89570188f 100644
--- 
a/sdks/java/io/iceberg/src/test/java/org/apache/beam/sdk/io/iceberg/cdc/sink/CommitterMetricsTest.java
+++ 
b/sdks/java/io/iceberg/src/test/java/org/apache/beam/sdk/io/iceberg/cdc/sink/CommitterMetricsTest.java
@@ -69,20 +69,17 @@ public class CommitterMetricsTest {
         .build();
   }
 
-  // Counts, records, and bytes cover both file kinds; only equality deletes 
count as delete
-  // records.
+  // The summary keeps both file lists for the per-file metrics.
   @Test
-  public void summarizesCountsRecordsAndBytes() {
-    CommitSummary summary =
-        CommitSummary.of(
-            ImmutableList.of(dataFile(SPEC_0, 10, 100), dataFile(SPEC_0, 20, 
200)),
-            ImmutableList.of(deleteFile(SPEC_0, true, 4, 40), 
deleteFile(SPEC_0, false, 7, 70)));
+  public void keepsFilesAndFlagsEqualityDeletes() {
+    ImmutableList<DataFile> dataFiles =
+        ImmutableList.of(dataFile(SPEC_0, 10, 100), dataFile(SPEC_0, 20, 200));
+    ImmutableList<DeleteFile> deleteFiles =
+        ImmutableList.of(deleteFile(SPEC_0, true, 4, 40), deleteFile(SPEC_0, 
false, 7, 70));
+    CommitSummary summary = CommitSummary.of(dataFiles, deleteFiles);
 
-    assertThat(summary.dataFileCount, equalTo(2L));
-    assertThat(summary.deleteFileCount, equalTo(2L));
-    assertThat(summary.dataRecords, equalTo(30L));
-    assertThat(summary.equalityDeleteRecords, equalTo(4L));
-    assertThat(summary.bytes, equalTo(410L));
+    assertThat(summary.dataFiles, equalTo(dataFiles));
+    assertThat(summary.deleteFiles, equalTo(deleteFiles));
     assertThat(summary.hasEqualityDeletes, equalTo(true));
   }
 
@@ -92,9 +89,7 @@ public class CommitterMetricsTest {
     CommitSummary summary =
         CommitSummary.of(ImmutableList.of(), 
ImmutableList.of(deleteFile(SPEC_0, false, 7, 70)));
 
-    assertThat(summary.equalityDeleteRecords, equalTo(0L));
     assertThat(summary.hasEqualityDeletes, equalTo(false));
-    assertThat(summary.bytes, equalTo(70L));
   }
 
   // The first spec id comes from the data files when there are any, and every 
spec id is collected.
@@ -116,8 +111,8 @@ public class CommitterMetricsTest {
   public void emptyWindowHasNoSpec() {
     CommitSummary summary = CommitSummary.of(ImmutableList.of(), 
ImmutableList.of());
 
-    assertThat(summary.dataFileCount, equalTo(0L));
-    assertThat(summary.bytes, equalTo(0L));
+    assertThat(summary.dataFiles, empty());
+    assertThat(summary.deleteFiles, empty());
     assertThat(summary.firstSpecId, nullValue());
     assertThat(summary.specIds, empty());
   }

Reply via email to