liveBook · Manning

Branch

As explained in Data Pipelines with Apache Airflow, Second Edition

Branching is the conditional selection of one of multiple execution paths within a workflow task or structure.

Suppose a sales pipeline must process data from two ERP systems, with the source determined by the DAG run’s data interval. Branching selects the appropriate execution path. If both systems need nearly identical fetch and clean steps, a PythonOperator can select code paths inside those tasks without changing the DAG structure. section 6.2.1

Internal branching becomes less useful when the systems require different chains of tasks: the DAG view cannot show which code path ran, and general PythonOperators may replace more specialized operators. Define separate task paths when those differences matter operationally. section 6.2.1

Choose an ERP path in the DAG

Create an old fetch-to-clean path and a new fetch-to-clean path. Put a BranchPythonOperator before them; its callable returns "fetch_sales_old" when data_interval_start precedes ERP_CHANGE_DATE, or "fetch_sales_new" otherwise. Connect the branch task to both fetch tasks. It may also return a list of downstream task IDs to select multiple tasks. section 6.2.2

Listing 6.11 Adding the branching condition function
def _pick_erp_system(**context):
    if context["data_interval_start "] < ERP_CHANGE_DATE:
        return "fetch_sales_old"
    else:
        return "fetch_sales_new"

pick_erp_system = BranchPythonOperator(
    task_id="pick_erp_system",
    python_callable=_pick_erp_system,
)

pick_erp_system >> [fetch_sales_old, fetch_sales_new]

The branching callable and its downstream dependencies show how a run selects the old or new fetch path. section 6.2.2

Take care where the paths rejoin. The unchosen path is skipped, so a join task using the default all_success trigger rule is also skipped: not all its upstream tasks succeeded. Set trigger_rule="none_failed" on the joining task. Alternatively, place an EmptyOperator with that rule between both clean tasks and the existing downstream task; this makes the join explicit without changing the downstream task’s rule. section 6.2.2

Listing 6.15 Adding an empty join task for clarity
from airflow.providers.standard.operators.empty import EmptyOperator

join_branch = EmptyOperator(
    task_id="join_erp_branch",
    trigger_rule="none_failed"
)

[clean_sales_old, clean_sales_new] >> join_branch
join_branch >> join_datasets

The empty join task collects both paths and applies none_failed before the workflow continues. section 6.2.2

How Data Pipelines with Apache Airflow, Second Edition covers it

How deep it goes
Works through branching inside tasks and in the DAG, including downstream selection, skipped tasks, and trigger rules at joins.
Examples
Uses an ERP migration with date-selected old and new fetch-and-clean paths, followed by an explicit join.
What it assumes
Assumes familiarity with a basic Airflow DAG and simple task dependencies; develops more complex branches and joins from there.
What you can do after
Build it from scratch using BranchPythonOperator to select task paths and a none_failed join to continue after a skipped path.
How it connects
Connects task dependencies, PythonOperator, BranchPythonOperator, trigger rules, skipped tasks, and joins.

Also explained in

Compare how 6 books explain branch →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Data Pipelines with Apache Airflow, Second Edition. The section links point to the parts of the book it draws on.