airflow-core/docs/administration-and-deployment/scheduler.rst
.. Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
.. http://www.apache.org/licenses/LICENSE-2.0
.. Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
.. _scheduler:
The Airflow scheduler monitors all tasks and Dags, then triggers the task instances once their dependencies are complete. Behind the scenes, the scheduler spins up a subprocess, which monitors and stays in sync with all Dags in the specified Dag directory. Once per minute, by default, the scheduler collects Dag parsing results and checks whether any active tasks can be triggered.
The Airflow scheduler is designed to run as a persistent service in an
Airflow production environment. To kick it off, all you need to do is
execute the airflow scheduler command. It uses the configuration specified in
airflow.cfg.
The scheduler uses the configured :doc:Executor <../core-concepts/executor/index> to run tasks that are ready.
To start a scheduler, simply run the command:
.. code-block:: bash
airflow scheduler
Your Dags will start executing once the scheduler is running successfully.
.. note::
The first Dag Run is created based on the minimum ``start_date`` for the tasks in your Dag.
Subsequent Dag Runs are created according to your Dag's :doc:`timetable <../authoring-and-scheduling/timetable>`.
For Dags with a cron or timedelta schedule, scheduler won't trigger your tasks until the period it covers has ended e.g., A job with schedule set as @daily runs after the day
has ended. This technique makes sure that whatever data is required for that period is fully available before the Dag is executed.
In the UI, it appears as if Airflow is running your tasks a day late
.. note::
If you run a Dag on a ``schedule`` of one day, the run with data interval starting on ``2019-11-21`` triggers after ``2019-11-21T23:59``.
**Let's Repeat That**, the scheduler runs your job one ``schedule`` AFTER the start date, at the END of the interval.
You should refer to :doc:`../core-concepts/dag-run` for details on scheduling a Dag.
.. note::
The scheduler is designed for high throughput. This is an informed design decision to achieve scheduling
tasks as soon as possible. The scheduler checks how many free slots available in a pool and schedule at most that number of tasks instances in one iteration.
This means that task priority will only come into effect when there are more scheduled tasks
waiting than the queue slots. Thus there can be cases where low priority tasks will be scheduled before high priority tasks if they share the same batch.
For more read about that you can reference this GitHub discussion <https://github.com/apache/airflow/discussions/28809>__.
.. _scheduler:ha:
.. versionadded: 2.0.0
Airflow supports running more than one scheduler concurrently -- both for performance reasons and for resiliency.
Overview """"""""
The :abbr:HA (highly available) scheduler is designed to take advantage of the existing metadata database.
This was primarily done for operational simplicity: every component already has to speak to this DB, and by
not using direct communication or consensus algorithm between schedulers (Raft, Paxos, etc.) nor another
consensus tool (Apache Zookeeper, or Consul for instance) we have kept the "operational surface area" to a
minimum.
The scheduler now uses the serialized Dag representation to make its scheduling decisions and the rough outline of the scheduling loop is:
This does, however, place some requirements on the Database.
.. _scheduler:ha:db_requirements:
Database Requirements """""""""""""""""""""
The short version is that users of PostgreSQL 12+ or MySQL 8.0+ are all ready to go -- you can start running as many copies of the scheduler as you like -- there is no further set up or config options needed. If you are using a different database please read on.
To maintain performance and throughput there is one part of the scheduling loop that does a number of
calculations in memory (because having to round-trip to the DB for each TaskInstance would be too slow) so we
need to ensure that only a single scheduler is in this critical section at once - otherwise limits would not
be correctly respected. To achieve this we use database row-level locks (using SELECT ... FOR UPDATE).
This critical section is where TaskInstances go from scheduled state and are enqueued to the executor, whilst
ensuring the various concurrency and pool limits are respected. The critical section is obtained by asking for
a row-level write lock on every row of the Pool table (roughly equivalent to SELECT * FROM slot_pool FOR UPDATE NOWAIT but the exact query is slightly different).
The following databases are fully supported and provide an "optimal" experience:
.. warning::
MariaDB did not implement the SKIP LOCKED or NOWAIT SQL clauses until version
10.6.0 <https://jira.mariadb.org/browse/MDEV-25433>_.
Without these features, running multiple schedulers is not supported and deadlock errors have been reported. MariaDB
10.6.0 and following may work appropriately with multiple schedulers, but this has not been tested.
.. note::
Microsoft SQL Server has not been tested with HA.
.. _fine-tuning-scheduler:
What impacts scheduler's performance """"""""""""""""""""""""""""""""""""
The Scheduler is responsible for continuously scheduling tasks for execution. In order to fine-tune your scheduler, you need to include a number of factors:
The kind of deployment you have
The logic and definition of your Dag structure:
The scheduler configuration
In order to perform fine-tuning, it's good to understand how Scheduler works under-the-hood.
You can take a look at the Airflow Summit 2021 talk
Deep Dive into the Airflow Scheduler talk <https://youtu.be/DYC4-xElccE>_ to perform the fine-tuning.
How to approach Scheduler's fine-tuning """""""""""""""""""""""""""""""""""""""
Airflow gives you a lot of "knobs" to turn to fine tune the performance but it's a separate task, depending on your particular deployment, your Dag structure, hardware availability and expectations, to decide which knobs to turn to get best effect for you. Part of the job when managing the deployment is to decide what you are going to optimize for.
Airflow gives you the flexibility to decide, but you should find out what aspect of performance is most important for you and decide which knobs you want to turn in which direction.
Generally for fine-tuning, your approach should be the same as for any performance improvement and optimizations (we will not recommend any specific tools - just use the tools that you usually use to observe and monitor your systems):
What resources might limit Scheduler's performance """"""""""""""""""""""""""""""""""""""""""""""""""
There are several areas of resource usage that you should pay attention to:
PGBouncer <https://www.pgbouncer.org/>_ as a proxy to your database. The :doc:helm-chart:index
supports PGBouncer out-of-the-box.working memory (names might vary depending on your deployment) rather
than total memory used.What can you do, to improve Scheduler's performance """""""""""""""""""""""""""""""""""""""""""""""""""
When you know what your resource usage is, the improvements that you can consider might be:
.. _scheduler:ha:tunables:
Scheduler Configuration options """""""""""""""""""""""""""""""
The following config settings can be used to control aspects of the Scheduler.
However, you can also look at other non-performance-related scheduler configuration parameters available at
:doc:../configurations-ref in the [scheduler] section.
:ref:config:scheduler__max_dagruns_to_create_per_loop
This changes the number of Dags that are locked by each scheduler when creating Dag runs. One possible reason for setting this lower is if you have huge Dags (in the order of 10k+ tasks per Dag) and are running multiple schedulers, you won't want one scheduler to do all the work.
:ref:config:scheduler__max_dagruns_per_loop_to_schedule
How many DagRuns should a scheduler examine (and lock) when scheduling and queuing tasks. Increasing this limit will allow more throughput for smaller Dags but will likely slow down throughput for larger (>500 tasks for example) Dags. Setting this too high when using multiple schedulers could also lead to one scheduler taking all the Dag runs leaving no work for the others.
:ref:config:scheduler__use_row_level_locking
Should the scheduler issue SELECT ... FOR UPDATE in relevant queries.
If this is set to False then you should not run more than a single
scheduler at once.
:ref:config:scheduler__pool_metrics_interval
How often (in seconds) should pool usage stats be sent to StatsD (if statsd_on is enabled). This is a relatively expensive query to compute this, so this should be set to match the same period as your StatsD roll-up period.
:ref:config:scheduler__ti_metrics_interval
How often (in seconds) should task instance (scheduled, queued, running and deferred) stats be sent to StatsD (if statsd_on is enabled). This is a relatively expensive query to compute this, so this should be set to match the same period as your StatsD roll-up period.
:ref:config:scheduler__orphaned_tasks_check_interval
How often (in seconds) should the scheduler check for orphaned tasks or dead SchedulerJobs.
This setting controls how a dead scheduler will be noticed and the tasks it was "supervising" get picked up by another scheduler. The tasks will stay running, so there is no harm in not detecting this for a while.
When a SchedulerJob is detected as "dead" (as determined by
:ref:config:scheduler__scheduler_health_check_threshold) any running or
queued tasks that were launched by the dead process will be "adopted" and
monitored by this scheduler instead.
:ref:config:scheduler__max_tis_per_query
The batch size of queries in the scheduling main loop. This should not be greater than
core.parallelism. If this is too high then SQL query performance may be impacted by
complexity of query predicate, and/or excessive locking.
Additionally, you may hit the maximum allowable query length for your db.
Set this to 0 to use the value of core.parallelism.
:ref:config:scheduler__scheduler_idle_sleep_time
Controls how long the scheduler will sleep between loops, but if there was nothing to do
in the loop. i.e. if it scheduled something then it will start the next loop
iteration straight away. This parameter is badly named (historical reasons) and it will be
renamed in the future with deprecation of the current name.