
The team needed a repeatable, scheduled data-warehousing workflow on GCP — raw files landing in cloud storage, then cleaned, loaded and aggregated into BigQuery without anyone touching it manually. Doing this by hand doesn't scale: it's slow, inconsistent, and the moment someone forgets to run a step the downstream reporting is stale or wrong.
An end-to-end automated ETL pipeline on Cloud Composer (Google's managed Apache Airflow), split into five DAGs so each stage is independently retryable and observable, running daily on a schedule:
| # | DAG | What it does |
|---|---|---|
| 1 | data_ingestion_dag | Reads the raw vgsales.csv from a GCS bucket and writes a combined dataset back to storage |
| 2 | data_cleaning_dag | Deduplicates and drops incomplete records, writing a processed_dataset.csv |
| 3 | bigquery_dag | Loads GCS → BigQuery via GCSToBigQueryOperator with an explicit 11-field schema and WRITE_TRUNCATE |
| 4 | bigquery_vgsales_aggregation_dag | Runs a CREATE OR REPLACE TABLE … AS SELECT to build a summary table aggregating sales by year and genre |
| 5 | (environment DAG) | Installs/pins the Python dependencies the workers need so tasks run reliably |
16,598 raw records flow through ingestion → preprocessing → transformation → BigQuery load on every run. Cleaning drops 307 incomplete records, so 16,291 rows land in the curated table, and the aggregation builds a 389-row summary table grouped by year and genre. (De-duplication removes nothing on this particular source — it's already unique — but the step stays in the DAG so a future dirtier extract can't silently double-count.)
retries and a retry_delay, so a transient failure in one stage doesn't force a full re-run.Rank, Name, Platform, Year, Genre, Publisher, and the five regional/global sales measures) rather than relying on autodetect.WRITE_TRUNCATE plus skip_leading_rows means re-running the pipeline produces the same table, never duplicates.BigQueryInsertJobOperator builds the curated summary_table with SQL inside BigQuery, so the heavy grouping runs where the data lives instead of being pulled into Python.Two engineering problems had to be solved for the pipeline to run dependably in a managed Airflow environment — both are the kind of thing that decides whether a pipeline survives production:
WRITE_TRUNCATE, which made every load validated and idempotent. I then added pre-aggregated summary tables so downstream queries read clean, typed columns instead of scanning raw data.install_pandas / install_airflow tasks in the DAG and custom test scripts to verify module versions.Throughout, I kept my manager and stakeholders updated on progress and risks early, with root causes and a mitigation plan, rather than surfacing problems late.
install_pandas / install_airflow dependency-provisioning tasks.pipeline-run-demo.html) — replays a full scheduled run: all five DAGs moving through the scheduler, a task failing and recovering on retry, and the BigQuery tables it produces. Real task IDs, real retry settings, and the real vgsales row counts; the UI itself is a mockup.