First DAG: 3 Tasks, 1 Schedule

Concepts covered: DAG Orchestration

The pipeline is the smallest version of the orders chain: extract, clean, publish, started at 01:00 with 1 retry policy for every task. The code below is illustrative. It has a name and a schedule, the retry policy, and 3 tasks joined by 2 edges, which is the shared shape; each orchestrator spells those same parts in its own syntax.

from orchestrator import DAG, task

with DAG(
    dag_id="daily_orders",
    schedule="0 1 * * *",
    retries=2,
    retry_delay_minutes=5,
) as dag:
    extract = task("extract_orders", command="python extract.py")
    clean = task("clean_orders", command="python clean.py")
    publish = task("publish_orders", command="python publish.py")

    extract >> clean >> publish

Reading it line by line

What each line declares

Cron fieldValueMeaning
minute0at minute 0
hour1of hour 1, so 01:00
day of month*every day
month*every month
day of week*every day of the week

The order of execution

The orchestrator reads the edges, and the order the tasks were written in plays no part. extract has no upstream edge, so it is the only task ready at 01:00. clean has 1 upstream edge from extract, so it becomes ready when extract succeeds. publish has 1 upstream edge from clean. With 3 tasks and 2 edges there is 1 possible order, and the orchestrator finds it by asking on each loop which task has nothing unfinished above it.

TimeEvent on a normal night
01:00Run created; extract_orders attempt 1 starts
01:40extract_orders succeeds; clean_orders starts
02:00clean_orders succeeds; publish_orders starts
02:05publish_orders succeeds; run marked success

You add a fourth task, extract_customers. publish_orders reads its output, but clean_orders does not. Which single change starts every task as soon as its inputs exist?

A night with a failure

Now the source database drops the connection 12 minutes in. The orchestrator marks attempt 1 failed, waits the 5 minute delay, and starts attempt 2 at 01:17. The whole extract runs again from the first row, succeeds at 01:57, and clean becomes ready. The dashboard is 17 minutes later than usual and correct. Nobody was paged, and the run page shows 2 attempts on extract_orders with the error sitting in the first attempt's log.

TimeEvent on the failure night
01:00extract_orders attempt 1 starts
01:12attempt 1 fails: connection reset
01:17attempt 2 starts after the 5 minute delay
01:57attempt 2 succeeds; clean_orders starts
02:17clean_orders succeeds; publish_orders starts
02:22publish_orders succeeds; run marked success

If attempts 2 and 3 fail as well, the sequence ends differently. After the third failure the orchestrator marks extract_orders failed and sets clean_orders and publish_orders to upstream-failed without starting either. It then fails the run and sends the alert. The published table keeps yesterday's rows, which is the right outcome: a stale table that everyone knows is stale beats a fresh-looking table built from the wrong night.

Do
  • Name tasks after what they produce, so the run page reads as the pipeline
  • Give every task a retry count, even 1, so a flaky connection does not page anyone
  • Let the edges decide order; never encode order in start times
  • Read the run page after the first night and note each task's duration
Avoid
  • Chain tasks by giving each its own clock time inside one DAG
  • Set so many retries that a broken source gets queried again and again for hours
  • Move data through the orchestrator's own workers
  • Put 2 unrelated pipelines in 1 DAG because they share a schedule

Capstone

extract_orders succeeds at 01:40. clean_orders then fails on all 3 attempts because a column it expects is missing. At 02:30, what is the state of publish_orders, and what does the dashboard show?

About this lesson

This section is part of the Orchestration: Beginner lesson on DataDriven, the home of data engineering. Lessons cover SQL, Python, Spark, data modeling and pipeline architecture, and each section pairs its explanation with worked examples and practice you run as you go.