You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iceberg.apache.org by li...@apache.org on 2025/03/11 09:15:02 UTC

(iceberg-rust) branch main updated: fix: refine doc for write support (#999)

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

liurenjie1024 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg-rust.git


The following commit(s) were added to refs/heads/main by this push:
     new 6c6ef747 fix: refine doc for write support (#999)
6c6ef747 is described below

commit 6c6ef7470b015dcafa5a2eb3f90709a0168f2a18
Author: ZENOTME <43...@users.noreply.github.com>
AuthorDate: Tue Mar 11 17:14:58 2025 +0800

    fix: refine doc for write support (#999)
    
    Part of #986. This PR adds more examples to refine the doc of writer.
    
    Co-authored-by: ZENOTME <st...@gmail.com>
---
 crates/iceberg/src/writer/mod.rs | 200 ++++++++++++++++++++++++++++++++++-----
 1 file changed, 174 insertions(+), 26 deletions(-)

diff --git a/crates/iceberg/src/writer/mod.rs b/crates/iceberg/src/writer/mod.rs
index 64357a0f..fabc871e 100644
--- a/crates/iceberg/src/writer/mod.rs
+++ b/crates/iceberg/src/writer/mod.rs
@@ -17,32 +17,180 @@
 
 //! Iceberg writer module.
 //!
-//! The writer API is designed to be extensible and flexible. Each writer is decoupled and can be create and config independently. User can:
-//! 1.Customize the writer using the writer trait.
-//! 2.Combine different writer to build a writer which have complex write logic.
-//!
-//! There are two kinds of writer:
-//! 1. FileWriter: Focus on writing record batch to different physical file format.(Such as parquet. orc)
-//! 2. IcebergWriter: Focus on the logical format of iceberg table. It will write the data using the FileWriter finally.
-//!
-//! # Simple example for data file writer:
-//! ```ignore
-//! // Create a parquet file writer builder. The parameter can get from table.
-//! let file_writer_builder = ParquetWriterBuilder::new(
-//!    0,
-//!    WriterProperties::builder().build(),
-//!    schema,
-//!    file_io.clone(),
-//!    loccation_gen,
-//!    file_name_gen,
-//! )
-//! // Create a data file writer using parquet file writer builder.
-//! let data_file_builder = DataFileBuilder::new(file_writer_builder);
-//! // Build the data file writer.
-//! let data_file_writer = data_file_builder.build().await.unwrap();
-//!
-//! data_file_writer.write(&record_batch).await.unwrap();
-//! let data_files = data_file_writer.flush().await.unwrap();
+//! This module contains the generic writer trait and specific writer implementation. We categorize the writer into two types:
+//! 1. FileWriter: writer for physical file format (Such as parquet, orc).
+//! 2. IcebergWriter: writer for logical format provided by iceberg table (Such as data file, equality delete file, position delete file)
+//!    or other function (Such as partition writer, delta writer).
+//!
+//! The IcebergWriter will use FileWriter to write underly physical file.
+//!
+//! We hope the writer interface can be extensible and flexible. Each writer can be create config independently
+//! and combined together to build a writer which have complex write logic. E.g. combine `FanoutPartitionWriter`, `DataFileWriter`, `ParquetWriter` to get
+//! a writer can split the data automatelly according to partition and write down as parquet physical format.
+//!
+//! For this purpose, there are four trait corresponding to these writer:
+//! - IcebergWriterBuilder
+//! - IcebergWriter
+//! - FileWriterBuilder
+//! - FileWriter
+//!
+//! User can create specific writer builder, combine them and build the writer finally. Also user can custom
+//! own writer and implement writer trait for them so that the custom writer can integrate with existing writer. (See following example)
+//!
+//! # Simple example for the data file writer used parquet physical format:
+//! ```rust, no_run
+//! use std::sync::Arc;
+//!
+//! use arrow_array::{ArrayRef, BooleanArray, Int32Array, RecordBatch, StringArray};
+//! use async_trait::async_trait;
+//! use iceberg::io::{FileIO, FileIOBuilder};
+//! use iceberg::spec::DataFile;
+//! use iceberg::transaction::Transaction;
+//! use iceberg::writer::base_writer::data_file_writer::DataFileWriterBuilder;
+//! use iceberg::writer::file_writer::location_generator::{
+//!     DefaultFileNameGenerator, DefaultLocationGenerator,
+//! };
+//! use iceberg::writer::file_writer::ParquetWriterBuilder;
+//! use iceberg::writer::{IcebergWriter, IcebergWriterBuilder};
+//! use iceberg::{Catalog, Result, TableIdent};
+//! use iceberg_catalog_memory::MemoryCatalog;
+//! use parquet::file::properties::WriterProperties;
+//! #[tokio::main]
+//! async fn main() -> Result<()> {
+//!     // Build your file IO.
+//!     let file_io = FileIOBuilder::new("memory").build()?;
+//!     // Connect to a catalog.
+//!     let catalog = MemoryCatalog::new(file_io, None);
+//!     // Load table from catalog.
+//!     let table = catalog
+//!         .load_table(&TableIdent::from_strs(["hello", "world"])?)
+//!         .await?;
+//!     let location_generator = DefaultLocationGenerator::new(table.metadata().clone()).unwrap();
+//!     let file_name_generator = DefaultFileNameGenerator::new(
+//!         "test".to_string(),
+//!         None,
+//!         iceberg::spec::DataFileFormat::Parquet,
+//!     );
+//!
+//!     // Create a parquet file writer builder. The parameter can get from table.
+//!     let parquet_writer_builder = ParquetWriterBuilder::new(
+//!         WriterProperties::default(),
+//!         table.metadata().current_schema().clone(),
+//!         table.file_io().clone(),
+//!         location_generator.clone(),
+//!         file_name_generator.clone(),
+//!     );
+//!     // Create a data file writer using parquet file writer builder.
+//!     let data_file_writer_builder = DataFileWriterBuilder::new(parquet_writer_builder, None);
+//!     // Build the data file writer
+//!     let mut data_file_writer = data_file_writer_builder.build().await.unwrap();
+//!
+//!     // Write the data using data_file_writer...
+//!
+//!     // Close the write and it will return data files back
+//!     let data_files = data_file_writer.close().await.unwrap();
+//!
+//!     Ok(())
+//! }
+//! ```
+//!
+//! # Custom writer to record latency
+//! ```rust, no_run
+//! use std::time::Instant;
+//!
+//! use arrow_array::RecordBatch;
+//! use iceberg::io::FileIOBuilder;
+//! use iceberg::spec::DataFile;
+//! use iceberg::writer::base_writer::data_file_writer::DataFileWriterBuilder;
+//! use iceberg::writer::file_writer::location_generator::{
+//!     DefaultFileNameGenerator, DefaultLocationGenerator,
+//! };
+//! use iceberg::writer::file_writer::ParquetWriterBuilder;
+//! use iceberg::writer::{IcebergWriter, IcebergWriterBuilder};
+//! use iceberg::{Catalog, Result, TableIdent};
+//! use iceberg_catalog_memory::MemoryCatalog;
+//! use parquet::file::properties::WriterProperties;
+//!
+//! #[derive(Clone)]
+//! struct LatencyRecordWriterBuilder<B> {
+//!     inner_writer_builder: B,
+//! }
+//!
+//! impl<B: IcebergWriterBuilder> LatencyRecordWriterBuilder<B> {
+//!     pub fn new(inner_writer_builder: B) -> Self {
+//!         Self {
+//!             inner_writer_builder,
+//!         }
+//!     }
+//! }
+//!
+//! #[async_trait::async_trait]
+//! impl<B: IcebergWriterBuilder> IcebergWriterBuilder for LatencyRecordWriterBuilder<B> {
+//!     type R = LatencyRecordWriter<B::R>;
+//!
+//!     async fn build(self) -> Result<Self::R> {
+//!         Ok(LatencyRecordWriter {
+//!             inner_writer: self.inner_writer_builder.build().await?,
+//!         })
+//!     }
+//! }
+//! struct LatencyRecordWriter<W> {
+//!     inner_writer: W,
+//! }
+//!
+//! #[async_trait::async_trait]
+//! impl<W: IcebergWriter> IcebergWriter for LatencyRecordWriter<W> {
+//!     async fn write(&mut self, input: RecordBatch) -> Result<()> {
+//!         let start = Instant::now();
+//!         self.inner_writer.write(input).await?;
+//!         let _latency = start.elapsed();
+//!         // record latency...
+//!         Ok(())
+//!     }
+//!
+//!     async fn close(&mut self) -> Result<Vec<DataFile>> {
+//!         let start = Instant::now();
+//!         let res = self.inner_writer.close().await?;
+//!         let _latency = start.elapsed();
+//!         // record latency...
+//!         Ok(res)
+//!     }
+//! }
+//!
+//! #[tokio::main]
+//! async fn main() -> Result<()> {
+//!     // Build your file IO.
+//!     let file_io = FileIOBuilder::new("memory").build()?;
+//!     // Connect to a catalog.
+//!     let catalog = MemoryCatalog::new(file_io, None);
+//!     // Load table from catalog.
+//!     let table = catalog
+//!         .load_table(&TableIdent::from_strs(["hello", "world"])?)
+//!         .await?;
+//!     let location_generator = DefaultLocationGenerator::new(table.metadata().clone()).unwrap();
+//!     let file_name_generator = DefaultFileNameGenerator::new(
+//!         "test".to_string(),
+//!         None,
+//!         iceberg::spec::DataFileFormat::Parquet,
+//!     );
+//!
+//!     // Create a parquet file writer builder. The parameter can get from table.
+//!     let parquet_writer_builder = ParquetWriterBuilder::new(
+//!         WriterProperties::default(),
+//!         table.metadata().current_schema().clone(),
+//!         table.file_io().clone(),
+//!         location_generator.clone(),
+//!         file_name_generator.clone(),
+//!     );
+//!     // Create a data file writer builder using parquet file writer builder.
+//!     let data_file_writer_builder = DataFileWriterBuilder::new(parquet_writer_builder, None);
+//!     // Create latency record writer using data file writer builder.
+//!     let latency_record_builder = LatencyRecordWriterBuilder::new(data_file_writer_builder);
+//!     // Build the final writer
+//!     let mut latency_record_data_file_writer = latency_record_builder.build().await.unwrap();
+//!
+//!     Ok(())
+//! }
 //! ```
 
 pub mod base_writer;