You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iceberg.apache.org by ga...@apache.org on 2026/03/28 03:51:42 UTC
(iceberg-cpp) branch main updated: feat: metrics for avro writer (#604)
This is an automated email from the ASF dual-hosted git repository.
gangwu pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg-cpp.git
The following commit(s) were added to refs/heads/main by this push:
new 181fc8ad feat: metrics for avro writer (#604)
181fc8ad is described below
commit 181fc8ad9accce9feb2e6c961fafc3db0cc5e0b7
Author: wzhuo <wn...@gmail.com>
AuthorDate: Sat Mar 28 11:51:37 2026 +0800
feat: metrics for avro writer (#604)
---
src/iceberg/CMakeLists.txt | 1 +
src/iceberg/avro/avro_metrics.cc | 31 +++++++++++++++++++++++++++
src/iceberg/avro/avro_metrics.h | 46 ++++++++++++++++++++++++++++++++++++++++
src/iceberg/avro/avro_writer.cc | 21 ++++++++++++------
src/iceberg/test/avro_test.cc | 38 +++++++++++++++++++++++++++++++++
5 files changed, 130 insertions(+), 7 deletions(-)
diff --git a/src/iceberg/CMakeLists.txt b/src/iceberg/CMakeLists.txt
index 646eae5d..69777953 100644
--- a/src/iceberg/CMakeLists.txt
+++ b/src/iceberg/CMakeLists.txt
@@ -183,6 +183,7 @@ if(ICEBERG_BUILD_BUNDLE)
avro/avro_data_util.cc
avro/avro_direct_decoder.cc
avro/avro_direct_encoder.cc
+ avro/avro_metrics.cc
avro/avro_reader.cc
avro/avro_writer.cc
avro/avro_register.cc
diff --git a/src/iceberg/avro/avro_metrics.cc b/src/iceberg/avro/avro_metrics.cc
new file mode 100644
index 00000000..82880c46
--- /dev/null
+++ b/src/iceberg/avro/avro_metrics.cc
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+#include "iceberg/avro/avro_metrics.h"
+
+namespace iceberg::avro {
+
+Metrics AvroMetrics::GetMetrics(const Schema& /*schema*/, int64_t num_records,
+ const MetricsConfig& /*metrics_config*/) {
+ // TODO(WZhuo) will populate in following PRs if datum writer is a
+ // MetricsAwareDatumWriter
+ return Metrics{.row_count = num_records};
+}
+
+} // namespace iceberg::avro
diff --git a/src/iceberg/avro/avro_metrics.h b/src/iceberg/avro/avro_metrics.h
new file mode 100644
index 00000000..799c00ee
--- /dev/null
+++ b/src/iceberg/avro/avro_metrics.h
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include "iceberg/iceberg_bundle_export.h"
+#include "iceberg/metrics.h"
+
+namespace iceberg {
+class Schema;
+class MetricsConfig;
+} // namespace iceberg
+
+namespace iceberg::avro {
+
+/// \brief Utility class for computing Avro file metrics.
+class ICEBERG_BUNDLE_EXPORT AvroMetrics {
+ public:
+ AvroMetrics() = delete;
+
+ /// \brief Compute metrics from writer state.
+ /// \param schema The Iceberg schema of the written data.
+ /// \param num_records The number of records written.
+ /// \param metrics_config The metrics configuration.
+ /// \return Metrics for the written Avro file.
+ static Metrics GetMetrics(const Schema& schema, int64_t num_records,
+ const MetricsConfig& metrics_config);
+};
+
+} // namespace iceberg::avro
diff --git a/src/iceberg/avro/avro_writer.cc b/src/iceberg/avro/avro_writer.cc
index 350530b3..32ce3f63 100644
--- a/src/iceberg/avro/avro_writer.cc
+++ b/src/iceberg/avro/avro_writer.cc
@@ -33,9 +33,11 @@
#include "iceberg/arrow/arrow_status_internal.h"
#include "iceberg/avro/avro_data_util_internal.h"
#include "iceberg/avro/avro_direct_encoder_internal.h"
+#include "iceberg/avro/avro_metrics.h"
#include "iceberg/avro/avro_register.h"
#include "iceberg/avro/avro_schema_util_internal.h"
#include "iceberg/avro/avro_stream_internal.h"
+#include "iceberg/metrics_config.h"
#include "iceberg/schema.h"
#include "iceberg/schema_internal.h"
#include "iceberg/util/checked_cast.h"
@@ -238,6 +240,7 @@ class AvroWriter::Impl {
ICEBERG_RETURN_UNEXPECTED(backend_->WriteRow(*write_schema_, *result, i));
}
+ num_records_ += result->length();
return {};
}
@@ -261,6 +264,14 @@ class AvroWriter::Impl {
return current_pos;
}
+ Result<Metrics> metrics() const {
+ if (!Closed()) {
+ return Invalid("AvroWriter is not closed");
+ }
+ return AvroMetrics::GetMetrics(*write_schema_, num_records_,
+ *MetricsConfig::Default());
+ }
+
private:
// The schema to write.
std::shared_ptr<::iceberg::Schema> write_schema_;
@@ -272,6 +283,8 @@ class AvroWriter::Impl {
ArrowSchema arrow_schema_;
// Total length of the written Avro file.
int64_t total_bytes_ = 0;
+ // Number of records written.
+ int64_t num_records_ = 0;
// The write backend to write data.
std::unique_ptr<AvroWriteBackend> backend_;
};
@@ -292,13 +305,7 @@ Status AvroWriter::Close() {
return {};
}
-Result<Metrics> AvroWriter::metrics() {
- if (impl_->Closed()) {
- // TODO(xiao.dong) implement metrics
- return {};
- }
- return Invalid("AvroWriter is not closed");
-}
+Result<Metrics> AvroWriter::metrics() { return impl_->metrics(); }
Result<int64_t> AvroWriter::length() { return impl_->length(); }
diff --git a/src/iceberg/test/avro_test.cc b/src/iceberg/test/avro_test.cc
index 518948d3..82da97ea 100644
--- a/src/iceberg/test/avro_test.cc
+++ b/src/iceberg/test/avro_test.cc
@@ -936,6 +936,44 @@ TEST_P(AvroWriterTest, MultipleAvroBlocks) {
}
}
+TEST_P(AvroWriterTest, Metrics) {
+ auto schema = std::make_shared<iceberg::Schema>(std::vector<SchemaField>{
+ SchemaField::MakeRequired(1, "id", std::make_shared<IntType>()),
+ SchemaField::MakeOptional(2, "name", std::make_shared<StringType>())});
+
+ std::string test_data = R"([[1, "Alice"], [2, "Bob"], [3, "Charlie"]])";
+
+ // Write data but don't close yet
+ ArrowSchema arrow_c_schema;
+ ASSERT_THAT(ToArrowSchema(*schema, &arrow_c_schema), IsOk());
+ auto arrow_schema = ::arrow::ImportType(&arrow_c_schema).ValueOrDie();
+ auto array = ::arrow::json::ArrayFromJSONString(arrow_schema, test_data).ValueOrDie();
+ struct ArrowArray arrow_array;
+ ASSERT_TRUE(::arrow::ExportArray(*array, &arrow_array).ok());
+
+ ICEBERG_UNWRAP_OR_FAIL(
+ writer_,
+ WriterFactoryRegistry::Open(
+ FileFormatType::kAvro,
+ {.path = temp_avro_file_, .schema = schema, .io = file_io_, .properties = {}}));
+ ASSERT_THAT(writer_->Write(&arrow_array), IsOk());
+
+ // Metrics should fail before close
+ ASSERT_THAT(writer_->metrics(), IsError(ErrorKind::kInvalid));
+
+ // After close, metrics should succeed
+ ASSERT_THAT(writer_->Close(), IsOk());
+ ICEBERG_UNWRAP_OR_FAIL(auto metrics, writer_->metrics());
+ ASSERT_TRUE(metrics.row_count.has_value());
+ EXPECT_EQ(metrics.row_count.value(), 3);
+ EXPECT_TRUE(metrics.column_sizes.empty());
+ EXPECT_TRUE(metrics.value_counts.empty());
+ EXPECT_TRUE(metrics.null_value_counts.empty());
+ EXPECT_TRUE(metrics.nan_value_counts.empty());
+ EXPECT_TRUE(metrics.lower_bounds.empty());
+ EXPECT_TRUE(metrics.upper_bounds.empty());
+}
+
// Instantiate parameterized tests for both direct encoder and GenericDatum paths
INSTANTIATE_TEST_SUITE_P(DirectEncoderModes, AvroWriterTest,
::testing::Values(true, false),