Apache Airflow Basics — Data Engineering

Apache Airflow Apache Airflow adalah platform orkestrasi workflow paling populer di data engineering. Airflow mendefinisikan pipeline sebagai DAG (Directed…

Apache Airflow adalah orchestrator: program yang menjalankan langkah-langkah pipeline kamu sesuai jadwal, dalam urutan yang benar, mengulang yang gagal, dan mencatat setiap run. Bayangin stasiun kereta. Jadwalnya bilang kereta paket berangkat tiap hari jam 00.00. Kepala stasiun melihat jam, memberangkatkan kereta, menahan kereta berikutnya kalau relnya belum kosong, dan mencatat tiap keberangkatan di buku. Kereta yang berangkat 2 Oktober jam 00.00 membawa paket tanggal 1 Oktober, jadi di karcisnya tertulis 1 Oktober. Jadwal itu DAG, kepala stasiun itu scheduler, satu keberangkatan itu satu run.

🧪 Tebakan cepat: DAG Toko Buku jalan tiap hari jam 00.00 WIB dan tiap run memuat penjualan satu hari. Run yang memuat penjualan 1 Oktober jalan kapan: 1 Oktober jam 00.00, atau 2 Oktober jam 00.00? Tebak dulu, jawabannya ada di bagian "Data interval dan logical date".

DAG, task, operator, scheduler

Versi terbaru Airflow per 11 Oktober 2026 adalah 3.3.2 (rilis 17 September 2026). Untuk pelajaran ini Airflow dipasang di virtual environment Python 3.13 dengan pip install "apache-airflow==3.3.2" --constraint ... (file constraint resmi untuk versi itu), sekitar 185 MB. Semua output di bawah dari instalasi itu.

DAG pertama Toko Buku

DAG ini menjalankan pipeline.py dari pelajaran sebelumnya, satu tanggal per run. Ditulis dengan TaskFlow API: fungsi Python biasa yang diberi decorator @dag dan @task.

from datetime import date, timedelta

import pendulum
from airflow.sdk import CronDataIntervalTimetable, dag, task

import pipeline  # pipeline.py dari pelajaran sebelumnya, satu folder dengan file ini

WIB = "Asia/Jakarta"


@dag(
    schedule=CronDataIntervalTimetable("0 0 * * *", timezone=WIB),
    start_date=pendulum.datetime(2026, 9, 28, tz=WIB),
    catchup=True,
    max_active_runs=1,
    default_args={"retries": 2, "retry_delay": timedelta(minutes=5)},
)
def toko_buku_harian():
    @task
    def tentukan_tanggal(data_interval_start=None) -> str:
        return data_interval_start.in_timezone(WIB).date().isoformat()

    @task
    def muat(tanggal: str) -> int:
        hari = date.fromisoformat(tanggal)
        bersih = pipeline.transform(pipeline.extract(hari))
        pipeline.load(bersih, hari)
        return len(bersih)

    @task
    def laporkan(tanggal: str, baris: int) -> None:
        print(f"penjualan {tanggal}: {baris} baris")

    tanggal = tentukan_tanggal()
    laporkan(tanggal, muat(tanggal))


toko_buku_harian()

Dijalankan sekali tanpa scheduler dengan airflow dags test toko_buku_harian 2026-10-02T00:00:00+07:00 (run manual memproses interval lengkap terakhir sebelum waktu itu, yaitu 1 Oktober), task muat mencatat log pipeline.py yang sama dengan pelajaran sebelumnya: extract 2026-10-01: 3 baris, 1 baris dobel dibuang, load 2026-10-01: 2 baris. Lalu laporkan mencetak penjualan 2026-10-01: 2 baris.

XCom: untuk nilai kecil saja

Nilai yang di-return sebuah task disimpan Airflow sebagai XCom (cross-communication) di database metadata-nya, lalu diberikan ke task yang membutuhkannya. Di run tadi, log Airflow mencatat Done. Returned value was: 2026-10-01 untuk tentukan_tanggal dan Done. Returned value was: 2 untuk muat: dua XCom, sebuah tanggal dan sebuah angka.

XCom dirancang untuk nilai kecil: tanggal, jumlah baris, path file. Data besarnya tetap di warehouse atau di file, dan task berikutnya membacanya dari sana. DataFrame jutaan baris yang dilewatkan lewat XCom membuat database metadata Airflow membengkak.

Data interval dan logical date

Tiap run terjadwal punya empat waktu:

Dihitung oleh Airflow 3.3.2 untuk DAG di atas (semua jam WIB):

Run jalan logical_date Data interval ds
29 Sep 00.00 28 Sep 00.00 28 Sep 00.00 sampai 29 Sep 00.00 2026-09-27
2 Okt 00.00 1 Okt 00.00 1 Okt 00.00 sampai 2 Okt 00.00 2026-09-30
11 Okt 00.00 10 Okt 00.00 10 Okt 00.00 sampai 11 Okt 00.00 2026-10-09

Run berlabel 1 Oktober jalan 2 Oktober jam 00.00, setelah harinya habis, karena baru saat itu semua pesanan 1 Oktober sudah ada. Itu jawaban tebakannya. Label yang tertinggal satu hari dari jam jalannya ini kebingungan klasik di Airflow.

Dua hal yang bikin makin licin:

Catchup dan backfill

DAG di atas dinyalakan 11 Oktober jam 21.00 WIB, padahal start_date-nya 28 September.

Tanggal lama bisa diisi kapan saja dengan backfill:

airflow backfill create --dag-id toko_buku_harian \
  --from-date 2026-09-28T00:00:00+07:00 --to-date 2026-10-09T00:00:00+07:00

Hasilnya 12 run, logical date 28 September sampai 9 Oktober, yang dijalankan scheduler dan kelihatan di UI. Tulis jam dan zona waktunya: tanggal polos 2026-09-28 dibaca sebagai tengah malam UTC (07.00 WIB), dan --dry-run menunjukkan run 28 September WIB ikut terlewat. Backfill dan catchup aman karena load di pipeline.py idempoten: tanggal yang dimuat ulang dihapus dulu.

Retry dan status

default_args={"retries": 2, "retry_delay": timedelta(minutes=5)} berlaku untuk semua task: yang gagal dicoba lagi 2 kali (total 3 percobaan) dengan jeda 5 menit. Selama menunggu, statusnya up_for_retry; kalau tetap gagal, failed, dan task sesudahnya jadi upstream_failed tanpa dijalankan. Hati-hati, retry-nya berlapis: pipeline.extract sendiri mencoba konek 3 kali, jadi sumber yang mati total dicoba 3 × 3 = 9 kali sebelum run dinyatakan gagal.

💡 Yang baru di Airflow 3

Airflow 3.0 keluar April 2025. Perubahan yang terasa bagi penulis DAG, menurut panduan resmi "Upgrading to Airflow 3":

⚠️ Jebakan yang sering ketemu

TL;DR: Airflow menjalankan pipeline sesuai jadwal: DAG (Directed Acyclic Graph) adalah task beserta urutannya tanpa putaran; operator menjalankan aksi, sensor menunggu kondisi; scheduler membuat run. Versi terbaru 3.3.2. DAG Toko Buku memakai TaskFlow (@dag, @task, import dari airflow.sdk) dan CronDataIntervalTimetable("0 0 * * *", timezone="Asia/Jakarta"), jadi run berlabel 1 Oktober (logical_date = awal interval) jalan 2 Oktober 00.00 setelah intervalnya selesai. ds itu tanggal UTC (2026-09-30 untuk run itu), dan "@daily" di Airflow 3 gak punya interval. XCom cuma untuk nilai kecil seperti tanggal dan jumlah baris. catchup=True membuat run untuk tiap interval yang terlewat sejak start_date (13 run), catchup=False (default) cuma yang terakhir, dan airflow backfill create dengan jam dan zona waktu mengisi tanggal lama. retries=2 berarti 3 percobaan; task harus idempoten.

Yang akan kamu pelajari