Penjadwalan

Mengurai preset jadwal, cron, data interval, dan logical date hingga perilaku catchup dapat diprediksi.

Daftar isi

Penjadwalan mengubah DAG dari kode yang dijalankan secara manual menjadi pipeline yang berjalan sendiri mengikuti waktu tertentu. Topik ini membahas parameter schedule beserta preset dan string cron, penerjemahan jadwal menjadi timetable, serta dua nilai yang menyertai setiap dag run, yaitu logical date dan data interval. Perilaku catchup yang menentukan apakah run untuk pemicuan yang terlewat tetap dibuat juga diuraikan, sehingga pemicuan DAG berikutnya dapat diprediksi dengan pasti.

Preset jadwal dan string cron#

Nilai parameter schedule pada decorator @dag menentukan kapan scheduler membuat dag run secara otomatis. Parameter ini menerima preset seperti @daily, string cron, atau None. Nilai None menonaktifkan penjadwalan sepenuhnya, sehingga DAG hanya dapat dipicu secara manual atau dari program lain.

Preset menyingkat ekspresi cron, yaitu notasi lima kolom untuk jadwal berulang yang paling sering digunakan. String cron memberi kendali penuh atas kelima kolom tersebut, yaitu menit, jam, hari dalam sebulan, bulan, dan hari dalam seminggu. Tabel berikut membandingkan preset bawaan dengan string cron yang setara.

PresetString cron setaraArti
@hourly0 * * * *Setiap awal jam
@daily0 0 * * *Setiap tengah malam
@weekly0 0 * * 0Setiap hari Minggu tengah malam
@monthly0 0 1 * *Setiap tanggal 1 tengah malam
@yearly0 0 1 1 *Setiap 1 Januari tengah malam

Berdasarkan urutan kolom tersebut, string "0 9 * * *" berarti setiap hari pukul 09:00. Tanda bintang menyatakan setiap nilai pada kolomnya. Jadwal dievaluasi terhadap zona waktu DAG, yang secara bawaan adalah UTC dan dapat diganti melalui konfigurasi default_timezone. Nilai schedule disimpan apa adanya, sehingga string yang ditulis dapat ditanyakan kembali dari objek DAG melalui atribut schedule, dan pemeriksaan itu dipraktikkan pada bagian berikutnya.

Timetable bawaan dan logical date#

String dan preset jadwal tidak langsung digunakan oleh scheduler, melainkan diterjemahkan menjadi objek timetable, yakni komponen yang memutuskan kapan sebuah dag run dibuat. Airflow 3 menggunakan CronTriggerTimetable untuk string cron, yaitu timetable yang memicu run pada setiap waktu yang cocok dengan ekspresi cron-nya. Setiap run yang dihasilkan membawa logical date, yaitu tanggal yang mengidentifikasi run tersebut pada kalender jadwal.

Berkas dag_laporan.py berikut membangun DAG laporan dengan dua task berurutan. Parameter schedule diisi string cron "0 9 * * *".

python dag_laporan.py
from airflow.providers.standard.operators.bash import BashOperator
from airflow.sdk import dag


@dag(
    dag_id="dag_laporan",
    schedule="0 9 * * *",
    description="Laporan harian yang berjalan setiap pukul 09:00.",
)
def dag_laporan():
    kumpul = BashOperator(task_id="kumpul_data", bash_command="echo 'Kumpul data'")
    susun = BashOperator(task_id="susun_laporan", bash_command="echo 'Susun laporan'")
    kumpul >> susun


dag_laporan()

Struktur jadwalnya dapat diperiksa dari objek DAG yang sudah dimuat. Atribut schedule mengembalikan string yang ditulis, sedangkan objek timetable menunjukkan hasil penerjemahannya.

bash
# arahkan metadata Airflow ke folder kerja sementara
export AIRFLOW_HOME=/tmp/ra-penjadwalan
# muat DAG lalu baca identitas, jadwal, dan timetable-nya
python3 -c "import dag_laporan; d = dag_laporan.dag_laporan(); print(d.dag_id); print(d.schedule); print(repr(d.timetable))"
text Keluaran
dag_laporan
0 9 * * *
CronTriggerTimetable(expression='0 9 * * *', timezone=Timezone('UTC'), interval=datetime.timedelta(0), run_immediately=False)

Keluaran memuat tiga baris yang saling melengkapi. Dua baris pertama menegaskan bahwa atribut schedule menyimpan string cron persis seperti yang ditulis. Baris ketiga menunjukkan bahwa scheduler bekerja dengan CronTriggerTimetable yang menggunakan zona waktu UTC, dan bagian interval=datetime.timedelta(0) menyatakan lebar data interval bawaan bernilai nol. Data interval itu sendiri diuraikan pada bagian berikutnya. Dengan lebar nol, logical date sebuah run sama dengan waktu pemicuannya, sehingga run yang dipicu pukul 09:00 pada tanggal 2026-11-04 membawa logical date 2026-11-04 09:00:00.

Data interval dan pola klasik#

Setiap dag run bekerja atas sebuah data interval, yaitu rentang waktu berbentuk [mulai, selesai) yang menyatakan periode data yang diproses. Notasi [mulai, selesai) berarti batas mulai termasuk di dalam rentang, sedangkan batas selesai tidak termasuk, sehingga dua interval berturut-turut tidak saling tumpang tindih. Airflow juga menyediakan CronDataIntervalTimetable, pola data interval klasik yang menjadi bawaan Airflow 2.x, dan pola inilah yang mengisi rentang tersebut secara penuh.

Pada pola klasik tersebut, batas selesai interval bertepatan dengan waktu pemicuan run, sedangkan logical date berpindah menjadi batas mulainya. Sebuah run cron "0 9 * * *" yang dipicu pada pagi 2026-11-04 dengan pola ini memproses data interval [2026-11-03 09:00, 2026-11-04 09:00). Run tersebut membawa logical date 2026-11-03 09:00:00, sehingga hari yang diproses adalah hari sebelumnya. Perhitungan yang sama pada jadwal @daily memberikan run yang dipicu tepat pada 2026-11-04 00:00 untuk interval [2026-11-03 00:00, 2026-11-04 00:00).

Batas-batas interval itu mudah dihitung ulang dengan pendulum, library tanggal dan waktu yang juga digunakan di dalam Airflow. Skrip berikut menghitung interval untuk run @daily pola klasik yang dipicu tepat tengah malam 2026-11-04.

python
import pendulum

# run @daily pola klasik yang dipicu tepat tengah malam 2026-11-04
pemicu = pendulum.datetime(2026, 11, 4, tz="UTC")

# interval berakhir pada waktu pemicuan dan berjarak satu hari
mulai = pemicu.subtract(days=1)

print(f"Pemicuan     : {pemicu.to_datetime_string()}")
print(f"Logical date : {mulai.to_datetime_string()}")
print(f"Data interval: [{mulai.to_datetime_string()}, {pemicu.to_datetime_string()})")
text Keluaran
Pemicuan     : 2026-11-04 00:00:00
Logical date : 2026-11-03 00:00:00
Data interval: [2026-11-03 00:00:00, 2026-11-04 00:00:00)

Keluaran menegaskan dua hal sekaligus. Interval run yang dipicu pada 2026-11-04 memang mencakup hari sebelumnya, sehingga task di dalamnya membaca data bertanggal 2026-11-03. Logical date yang dimiliki run justru merupakan tanggal data tersebut, bukan tanggal task benar-benar berjalan, dan perbedaan inilah sumber kebingungan yang paling sering muncul saat membaca riwayat run pada UI.

Catchup#

Catchup adalah perilaku scheduler yang membuat dag run untuk pemicuan yang terlewat, misalnya karena DAG baru diterapkan atau pernah nonaktif. Apabila catchup aktif, scheduler membuat run untuk setiap pemicuan yang terlewat sejak start_date DAG, yaitu batas awal kapan DAG dianggap berlaku. Apabila catchup tidak aktif, run pertama hanya dibuat untuk pemicuan berikutnya, sedangkan interval yang terlewat dibiarkan kosong hingga diisi secara manual.

Airflow 3 menonaktifkan catchup secara bawaan. Catchup tidak lagi menjadi argumen DAG seperti pada Airflow 2.x, yang bawaannya True. Pada Airflow 3, perilaku ini dikendalikan oleh konfigurasi catchup_by_default pada kelompok scheduler. Nilai bawaan tersebut dapat dibaca langsung dari perintah konfigurasi Airflow.

bash
# baca nilai bawaan catchup_by_default pada kelompok scheduler
airflow config get-value scheduler catchup_by_default
text Keluaran
False

Nilai False berarti DAG terjadwal yang baru diterapkan tidak langsung dibuatkan run untuk masa lalu, dan run pertamanya menunggu pemicuan berikutnya. Perbedaan perilaku ini perlu diperhatikan ketika memindahkan DAG dari Airflow 2.x, karena DAG yang dulu otomatis membuat run untuk riwayat kini berhenti pada pemicuan terdekat. Interval yang terlewat tetap dapat diisi kemudian melalui backfill, yaitu menjalankan run untuk interval tertentu secara manual.

Ringkasan#

  • Parameter schedule menerima preset, string cron, atau None, dan Airflow menerjemahkannya menjadi timetable yang memutuskan waktu pembuatan dag run.
  • Pada bawaan Airflow 3, string cron menjadi CronTriggerTimetable yang menjadikan logical date sama dengan waktu pemicuan run.
  • Data interval [mulai, selesai) menyatakan periode data yang diproses, sedangkan pola klasik CronDataIntervalTimetable membentuknya dari pemicuan sebelumnya hingga pemicuan berikutnya dengan logical date pada batas mulainya.
  • Catchup tidak aktif secara bawaan pada Airflow 3 melalui konfigurasi catchup_by_default, berbeda dari bawaan True pada Airflow 2.x, dan interval yang terlewat diisi melalui backfill.

Referensi#