Big Data Fundamentals: data pipeline tutorial



This content originally appeared on DEV Community and was authored by DevOps Fundamental

Building Robust Data Pipelines with Apache Iceberg: A Production Deep Dive

1. Introduction

The relentless growth of data volume and velocity presents a constant engineering challenge: maintaining query performance and data consistency in the face of evolving schemas and increasing data complexity. We recently faced this acutely while building a real-time fraud detection system for a large e-commerce platform. Initial attempts using traditional Hive tables on HDFS resulted in increasingly slow query times as the table grew, coupled with brittle schema evolution processes that frequently broke downstream applications. This necessitated a move towards a more modern table format capable of handling petabytes of data with sub-second query latency and seamless schema changes. This blog post details our journey adopting Apache Iceberg as the foundation for our data pipelines, focusing on architectural considerations, performance tuning, and operational best practices. We operate in a hybrid cloud environment leveraging AWS EMR with Spark for processing and S3 for storage. Our data volume is approximately 50TB/day, with a requirement for near real-time analytics (sub-second query latency for 95th percentile).

2. What is Apache Iceberg in Big Data Systems?

Apache Iceberg isn’t a data pipeline tool per se, but a high-performance table format designed for huge analytic datasets. It addresses the limitations of traditional Hive tables by providing ACID transactions, schema evolution, time travel, and efficient query planning. Unlike Hive, which relies on the metastore for metadata, Iceberg manages its own metadata in a hierarchical structure of manifest lists, manifest files, and data files. This allows for atomic updates, preventing partial writes and ensuring data consistency.

At a protocol level, Iceberg leverages Parquet as its primary data format, benefiting from its columnar storage and compression capabilities. It introduces a layer of metadata on top of Parquet, enabling features like hidden partitioning, which simplifies query optimization and reduces the need for manual partitioning schemes. Iceberg’s metadata is stored in a catalog (e.g., Hive Metastore, AWS Glue, Nessie) providing a unified view of the table’s state.

3. Real-World Use Cases

  • CDC Ingestion & Incremental Updates: We ingest change data capture (CDC) streams from our transactional databases (PostgreSQL) using Debezium and Kafka. Iceberg allows us to efficiently merge these incremental updates into our analytical tables without full table rewrites.
  • Streaming ETL: Real-time event data (clickstreams, app events) is processed by Flink and written to Iceberg tables. The transactional nature of Iceberg ensures that downstream analytics queries always see a consistent view of the data.
  • Large-Scale Joins: Joining large fact and dimension tables is a common operation. Iceberg’s metadata allows Spark to efficiently prune partitions and data files, significantly reducing the amount of data scanned during joins.
  • Schema Validation & Data Quality: We integrate Iceberg with Great Expectations to enforce data quality constraints during ingestion. Invalid records are routed to a dead-letter queue for investigation.
  • ML Feature Pipelines: Iceberg serves as the storage layer for our machine learning feature store. Time travel capabilities allow us to recreate historical feature sets for model retraining and backtesting.

4. System Design & Architecture

graph LR
    A[Transactional Databases (PostgreSQL)] --> B(Debezium/Kafka);
    B --> C{Flink Streaming Job};
    C --> D[Iceberg Table (S3)];
    E[Clickstream/App Events] --> C;
    F[Data Analysts/BI Tools] --> D;
    G[ML Training Pipelines] --> D;
    H[Great Expectations] --> C;
    H --> D;
    subgraph AWS EMR Cluster
        C
    end
    style D fill:#f9f,stroke:#333,stroke-width:2px

This diagram illustrates our core pipeline. CDC data and streaming events are processed by Flink and written to Iceberg tables stored in S3. Data analysts and ML pipelines query the Iceberg tables directly. Great Expectations validates data quality during ingestion.

We leverage EMR with Spark for batch processing tasks like data compaction and schema evolution. Our Iceberg catalog is AWS Glue. Partitioning is based on event time (event_time) and a hash of the user ID (user_id) to distribute data evenly across partitions. We utilize hidden partitioning for simplicity and query optimization.

5. Performance Tuning & Resource Management

Iceberg’s performance is heavily influenced by file size and partitioning. We’ve found the following configurations to be effective:

  • spark.sql.files.maxPartitionBytes: 134217728 (128MB) – Controls the maximum size of files that Spark will read into a single partition.
  • spark.sql.shuffle.partitions: 200 – Adjusts the number of partitions used during shuffle operations. Too few partitions can lead to data skew, while too many can increase overhead.
  • fs.s3a.connection.maximum: 1000 – Increases the number of concurrent connections to S3.
  • iceberg.compaction.max-file-count: 100 – Limits the number of files in a partition before compaction is triggered.

We regularly compact small files into larger ones to improve read performance. We also monitor data skew using Spark UI and adjust partitioning strategies accordingly. File size compaction is scheduled via Airflow. We’ve observed a 2x improvement in query latency after implementing a regular compaction schedule.

6. Failure Modes & Debugging

  • Data Skew: Uneven distribution of data across partitions can lead to performance bottlenecks. Monitor partition sizes using SELECT partition_spec.partition_values, COUNT(*) FROM table GROUP BY partition_spec.partition_values in Spark SQL. Consider salting the partitioning key to distribute data more evenly.
  • Out-of-Memory Errors: Large joins or aggregations can exhaust memory resources. Increase Spark executor memory (spark.executor.memory) or optimize the query plan.
  • Job Retries: Transient network errors or S3 outages can cause job failures. Configure Spark to automatically retry failed tasks.
  • DAG Crashes: Complex pipelines can be prone to DAG crashes. Use Spark UI to identify the failing stage and analyze the logs for errors.

Monitoring is crucial. We use Datadog to track Spark application metrics (memory usage, CPU utilization, shuffle read/write times) and S3 request latency. Alerts are configured to notify us of performance degradation or errors.

7. Data Governance & Schema Management

Iceberg’s schema evolution capabilities are a game-changer. We use a schema registry (Confluent Schema Registry) to manage schema changes. When a new schema version is deployed, we use Iceberg’s ALTER TABLE ADD COLUMNS command to add new columns without rewriting the entire table. We also leverage Iceberg’s ALTER TABLE RENAME COLUMN to rename columns.

We integrate Iceberg with AWS Glue Data Catalog for metadata management. Data lineage is tracked using a custom solution that captures Iceberg metadata changes. Data quality checks are enforced using Great Expectations, and failing records are quarantined.

8. Security and Access Control

We leverage AWS IAM roles and policies to control access to S3 buckets and EMR clusters. S3 bucket policies are configured to restrict access to authorized users and roles. We also use AWS Lake Formation to manage fine-grained access control at the table and column level. Data is encrypted at rest using S3 server-side encryption (SSE-S3).

9. Testing & CI/CD Integration

We use Great Expectations to validate data quality during pipeline development and deployment. We also write unit tests for our Spark jobs using the Spark testing framework. Our CI/CD pipeline (Jenkins) includes the following steps:

  • Linting: Check code style and syntax.
  • Unit Tests: Run unit tests for Spark jobs.
  • Data Quality Tests: Run Great Expectations data quality checks.
  • Staging Deployment: Deploy the pipeline to a staging environment.
  • Regression Tests: Run regression tests to verify that the pipeline is functioning correctly.
  • Production Deployment: Deploy the pipeline to production.

10. Common Pitfalls & Operational Misconceptions

  • Ignoring Compaction: Small files lead to performance degradation. Regular compaction is essential.
  • Over-Partitioning: Too many partitions can increase metadata overhead and slow down query planning.
  • Underestimating Schema Evolution Complexity: Carefully plan schema changes to avoid breaking downstream applications.
  • Not Monitoring Data Skew: Data skew can lead to performance bottlenecks.
  • Assuming Iceberg Solves All Problems: Iceberg is a powerful table format, but it doesn’t replace the need for good data modeling and pipeline design.

11. Enterprise Patterns & Best Practices

  • Data Lakehouse vs. Warehouse Tradeoffs: Iceberg enables a data lakehouse architecture, combining the flexibility of a data lake with the reliability of a data warehouse.
  • Batch vs. Micro-Batch vs. Streaming: Choose the appropriate processing paradigm based on latency requirements.
  • File Format Decisions: Parquet is generally the best choice for analytical workloads.
  • Storage Tiering: Use S3 lifecycle policies to move infrequently accessed data to cheaper storage tiers.
  • Workflow Orchestration: Airflow or Dagster are essential for managing complex data pipelines.

12. Conclusion

Apache Iceberg has been instrumental in building a scalable and reliable data platform for our e-commerce business. Its transactional capabilities, schema evolution features, and efficient query planning have significantly improved query performance and data consistency. Moving forward, we plan to benchmark different compaction strategies, introduce schema enforcement using a schema registry, and explore migrating to a more modern data governance framework. The key takeaway is that investing in a robust table format like Iceberg is crucial for building a future-proof data infrastructure.


This content originally appeared on DEV Community and was authored by DevOps Fundamental