Tasks

A Task is the basic unit of execution in Airflow. Tasks are arranged into Dags, and then have upstream and downstream dependencies set between them in order to express the order they should run in.

There are three basic kinds of Task:

  • Operators, predefined task templates that you can string together quickly to build most parts of your Dags.

  • Sensors, a special subclass of Operators which are entirely about waiting for an external event to happen.

  • A TaskFlow-decorated @task, which is a custom Python function packaged up as a Task.

Internally, these are all actually subclasses of Airflow’s BaseOperator, and the concepts of Task and Operator are somewhat interchangeable, but it’s useful to think of them as separate concepts - essentially, Operators and Sensors are templates, and when you call one in a Dag file, you’re making a Task.

Relationships

The key part of using Tasks is defining how they relate to each other - their dependencies, or as we say in Airflow, their upstream and downstream tasks. You declare your Tasks first, and then you declare their dependencies second.

Note

We call the upstream task the one that is directly preceding the other task. We used to call it a parent task before. Be aware that this concept does not describe the tasks that are higher in the tasks hierarchy (i.e. they are not a direct parents of the task). Same definition applies to downstream task, which needs to be a direct child of the other task.

There are two ways of declaring dependencies - using the >> and << (bitshift) operators:

first_task >> second_task >> [third_task, fourth_task]

Or the more explicit set_upstream and set_downstream methods:

first_task.set_downstream(second_task)
third_task.set_upstream(second_task)

These both do exactly the same thing, but in general we recommend you use the bitshift operators, as they are easier to read in most cases.

By default, a Task will run when all of its upstream (parent) tasks have succeeded, but there are many ways of modifying this behaviour to add branching, to only wait for some upstream tasks, or to change behaviour based on where the current run is in history. For more, see Control Flow.

Tasks don’t pass information to each other by default, and run entirely independently. If you want to pass information from one Task to another, you should use XComs.

Task Instances

Much in the same way that a Dag is instantiated into a Dag Run each time it runs, the tasks under a Dag are instantiated into Task Instances.

An instance of a Task is a specific run of that task for a given Dag (and thus for a given data interval). They are also the representation of a Task that has state, representing what stage of the lifecycle it is in.

The possible states for a Task Instance are:

  • none: The Task has not yet been queued for execution (its dependencies are not yet met)

  • scheduled: The scheduler has determined the Task’s dependencies are met and it should run

  • queued: The task has been assigned to an Executor and is awaiting a worker

  • running: The task is running on a worker (or on a local/synchronous executor)

  • success: The task finished running without errors

  • restarting: The task was externally requested to restart when it was running

  • failed: The task had an error during execution and failed to run

  • skipped: The task was skipped due to branching, LatestOnly, or similar.

  • upstream_failed: An upstream task failed and the Trigger Rule says we needed it

  • up_for_retry: The task failed, but has retry attempts left and will be rescheduled.

  • up_for_reschedule: The task is a Sensor that is in reschedule mode

  • deferred: The task has been deferred to a trigger

  • awaiting_input: The task is a Human-in-the-loop task waiting for a human response. It is managed by the scheduler and uses neither a worker slot nor the triggerer.

  • removed: The task has vanished from the Dag since the run started

../_images/diagram_task_lifecycle.png

Ideally, a task should flow from none, to scheduled, to queued, to running, and finally to success.

When any custom Task (Operator) is running, it will get a copy of the task instance passed to it; as well as being able to inspect task metadata, it also contains methods for things like XComs.

Relationship Terminology

For any given Task Instance, there are two types of relationships it has with other instances.

Firstly, it can have upstream and downstream tasks:

task1 >> task2 >> task3

When a Dag runs, it will create instances for each of these tasks that are upstream/downstream of each other, but which all have the same data interval.

There may also be instances of the same task, but for different data intervals - from other runs of the same Dag. We call these previous and next - it is a different relationship to upstream and downstream!

Note

Some older Airflow documentation may still use “previous” to mean “upstream”. If you find an occurrence of this, please help us fix it!

Timeouts

If you want a task to have a maximum runtime, set its execution_timeout attribute to a datetime.timedelta value that is the maximum permissible runtime. This applies to all Airflow tasks, including sensors. execution_timeout controls the maximum time allowed for every execution. If execution_timeout is breached, the task times out and AirflowTaskTimeout is raised.

In addition, sensors have a timeout parameter. This only matters for sensors in reschedule mode. timeout controls the maximum time allowed for the sensor to succeed. If timeout is breached, AirflowSensorTimeout will be raised and the sensor fails immediately without retrying.

The following SFTPSensor example illustrates this. The sensor is in reschedule mode, meaning it is periodically executed and rescheduled until it succeeds.

  • Each time the sensor pokes the SFTP server, it is allowed to take maximum 60 seconds as defined by execution_timeout.

  • If it takes the sensor more than 60 seconds to poke the SFTP server, AirflowTaskTimeout will be raised. The sensor is allowed to retry when this happens. It can retry up to 2 times as defined by retries.