# Buggy code
def _change_state_for_tis_without_dagrun(
    self, old_states: List[str], new_state: str, session: Session = None
) -> None:
    """
    For all DAG IDs in the DagBag, look for task instances in the
    old_states and set them to new_state if the corresponding DagRun
    does not exist or exists but is not in the running state. This
    normally should not happen, but it can if the state of DagRuns are
    changed manually.

    :param old_states: examine TaskInstances in this state
    :type old_states: list[airflow.utils.state.State]
    :param new_state: set TaskInstances to this state
    :type new_state: airflow.utils.state.State
    """
    tis_changed = 0
    query = (
        session.query(models.TaskInstance)
        .outerjoin(models.TaskInstance.dag_run)
        .filter(models.TaskInstance.dag_id.in_(list(self.dagbag.dag_ids)))
        .filter(models.TaskInstance.state.in_(old_states))
        .filter(
            or_(
                # pylint: disable=comparison-with-callable
                models.DagRun.state != State.RUNNING,
                # pylint: disable=no-member
                models.DagRun.state.is_(None),
            )
        )
    )
    # We need to do this for mysql as well because it can cause deadlocks
    # as discussed in https://issues.apache.org/jira/browse/AIRFLOW-2516
    if self.using_sqlite or self.using_mysql:
        tis_to_change: List[TI] = with_row_locks(
            query, of=TI, **skip_locked(session=session)
        ).all()
        for ti in tis_to_change:
            ti.set_state(new_state, session=session)
            tis_changed += 1
    else:
        subq = query.subquery()
        current_time = timezone.utcnow()
        ti_prop_update = {
            models.TaskInstance.state: new_state,
            models.TaskInstance.start_date: current_time,
        }

        # Only add end_date and duration if the new_state is 'success', 'failed' or 'skipped'
        if new_state in State.finished:
            ti_prop_update.update(
                {
                    models.TaskInstance.end_date: current_time,
                    models.TaskInstance.duration: 0,
                }
            )

        tis_changed = (
            session.query(models.TaskInstance)
            .filter(
                models.TaskInstance.dag_id == subq.c.dag_id,
                models.TaskInstance.task_id == subq.c.task_id,
                models.TaskInstance.execution_date == subq.c.execution_date,
            )
            .update(ti_prop_update, synchronize_session=False)
        )

    if tis_changed > 0:
        session.flush()
        self.log.warning(
            "Set %s task instances to state=%s as their associated DagRun was not in RUNNING state",
            tis_changed,
            new_state,
        )
        Stats.gauge("scheduler.tasks.without_dagrun", tis_changed)


# Traceback (_mysql_exceptions.OperationalError)
[2020-10-26 09:03:54,608] {{settings.py:49}} INFO - Configured default timezone Timezone('UTC')
[2020-10-26 09:04:05,467] {{scheduler_job.py:1327}} ERROR - Exception when executing SchedulerJob._run_scheduler_loop
Traceback (most recent call last):
File "/home/airflow/.local/lib/python3.8/site-packages/sqlalchemy/engine/base.py", line 1277, in _execute_context
self.dialect.do_execute(
File "/home/airflow/.local/lib/python3.8/site-packages/sqlalchemy/engine/default.py", line 593, in do_execute
cursor.execute(statement, parameters)
File "/home/airflow/.local/lib/python3.8/site-packages/MySQLdb/cursors.py", line 255, in execute
self.errorhandler(self, exc, value)
File "/home/airflow/.local/lib/python3.8/site-packages/MySQLdb/connections.py", line 50, in defaulterrorhandler
raise errorvalue
File "/home/airflow/.local/lib/python3.8/site-packages/MySQLdb/cursors.py", line 252, in execute
res = self._query(query)
File "/home/airflow/.local/lib/python3.8/site-packages/MySQLdb/cursors.py", line 379, in _query
self._do_get_result(db)
File "/home/airflow/.local/lib/python3.8/site-packages/MySQLdb/cursors.py", line 182, in _do_get_result
self._result = result = self._get_result()
File "/home/airflow/.local/lib/python3.8/site-packages/MySQLdb/cursors.py", line 411, in _get_result
return self._get_db().store_result()
_mysql_exceptions.OperationalError: (1213, 'Deadlock found when trying to get lock; try restarting transaction')

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
File "/home/airflow/.local/lib/python3.8/site-packages/airflow/jobs/scheduler_job.py", line 1308, in _execute
self._run_scheduler_loop()
File "/home/airflow/.local/lib/python3.8/site-packages/airflow/jobs/scheduler_job.py", line 1379, in _run_scheduler_loop
num_queued_tis = self._do_scheduling(session)
File "/home/airflow/.local/lib/python3.8/site-packages/airflow/jobs/scheduler_job.py", line 1451, in _do_scheduling
self._create_dag_runs(query.all(), session)
File "/home/airflow/.local/lib/python3.8/site-packages/sqlalchemy/orm/query.py", line 3341, in all
return list(self)
File "/home/airflow/.local/lib/python3.8/site-packages/sqlalchemy/orm/query.py", line 3503, in __iter__
return self._execute_and_instances(context)
File "/home/airflow/.local/lib/python3.8/site-packages/sqlalchemy/orm/query.py", line 3528, in _execute_and_instances
result = conn.execute(querycontext.statement, self._params)
File "/home/airflow/.local/lib/python3.8/site-packages/sqlalchemy/engine/base.py", line 1014, in execute
return meth(self, multiparams, params)
File "/home/airflow/.local/lib/python3.8/site-packages/sqlalchemy/sql/elements.py", line 298, in _execute_on_connection
return connection._execute_clauseelement(self, multiparams, params)
File "/home/airflow/.local/lib/python3.8/site-packages/sqlalchemy/engine/base.py", line 1127, in _execute_clauseelement
ret = self._execute_context(
File "/home/airflow/.local/lib/python3.8/site-packages/sqlalchemy/engine/base.py", line 1317, in _execute_context
self._handle_dbapi_exception(
File "/home/airflow/.local/lib/python3.8/site-packages/sqlalchemy/engine/base.py", line 1511, in _handle_dbapi_exception
util.raise_(
File "/home/airflow/.local/lib/python3.8/site-packages/sqlalchemy/util/compat.py", line 178, in raise_
raise exception
File "/home/airflow/.local/lib/python3.8/site-packages/sqlalchemy/engine/base.py", line 1277, in _execute_context
self.dialect.do_execute(
File "/home/airflow/.local/lib/python3.8/site-packages/sqlalchemy/engine/default.py", line 593, in do_execute
cursor.execute(statement, parameters)
File "/home/airflow/.local/lib/python3.8/site-packages/MySQLdb/cursors.py", line 255, in execute
self.errorhandler(self, exc, value)
File "/home/airflow/.local/lib/python3.8/site-packages/MySQLdb/connections.py", line 50, in defaulterrorhandler
raise errorvalue
File "/home/airflow/.local/lib/python3.8/site-packages/MySQLdb/cursors.py", line 252, in execute
res = self._query(query)
File "/home/airflow/.local/lib/python3.8/site-packages/MySQLdb/cursors.py", line 379, in _query
self._do_get_result(db)
File "/home/airflow/.local/lib/python3.8/site-packages/MySQLdb/cursors.py", line 182, in _do_get_result
self._result = result = self._get_result()
File "/home/airflow/.local/lib/python3.8/site-packages/MySQLdb/cursors.py", line 411, in _get_result
return self._get_db().store_result()
sqlalchemy.exc.OperationalError: (_mysql_exceptions.OperationalError) (1213, 'Deadlock found when trying to get lock; try restarting transaction')
[SQL: SELECT dag.dag_id AS dag_dag_id, dag.root_dag_id AS dag_root_dag_id, dag.is_paused AS dag_is_paused, dag.is_subdag AS dag_is_subdag, dag.is_active AS dag_is_active, dag.last_scheduler_run AS dag_last_scheduler_run, dag.last_pickled AS dag_last_pickled, dag.last_expired AS dag_last_expired, dag.scheduler_lock AS dag_scheduler_lock, dag.pickle_id AS dag_pickle_id, dag.fileloc AS dag_fileloc, dag.owners AS dag_owners, dag.description AS dag_description, dag.default_view AS dag_default_view, dag.schedule_interval AS dag_schedule_interval, dag.concurrency AS dag_concurrency, dag.has_task_concurrency_limits AS dag_has_task_concurrency_limits, dag.next_dagrun AS dag_next_dagrun, dag.next_dagrun_create_after AS dag_next_dagrun_create_after
FROM dag
WHERE dag.is_paused IS false AND dag.is_active IS true AND dag.next_dagrun_create_after <= now() ORDER BY dag.next_dagrun_create_after
LIMIT %s FOR UPDATE]
[parameters: (10,)]
(Background on this error at: http://sqlalche.me/e/13/e3q8)
[2020-10-26 09:04:06,512] {{process_utils.py:102}} INFO - Sending Signals.SIGTERM to GPID 7437
[2020-10-26 09:04:07,029] {{process_utils.py:68}} INFO - Process psutil.Process(pid=7762, status='terminated', started='09:04:05') (7762) terminated with exit code None
[2020-10-26 09:04:07,122] {{process_utils.py:219}} INFO - Waiting up to 5 seconds for processes to exit...
[2020-10-26 09:04:07,126] {{process_utils.py:68}} INFO - Process psutil.Process(pid=7774, status='terminated', started='09:04:05') (7774) terminated with exit code None
[2020-10-26 09:04:07,128] {{process_utils.py:68}} INFO - Process psutil.Process(pid=7437, status='terminated', exitcode=0, started='09:03:54') (7437) terminated with exit code 0
[2020-10-26 09:04:07,128] {{process_utils.py:68}} INFO - Process psutil.Process(pid=7775, status='terminated', started='09:04:05') (7775) terminated with exit code None
[2020-10-26 09:04:07,129] {{process_utils.py:68}} INFO - Process psutil.Process(pid=7773, status='terminated', started='09:04:05') (7773) terminated with exit code None
[2020-10-26 09:04:07,129] {{process_utils.py:68}} INFO - Process psutil.Process(pid=7782, status='terminated', started='09:04:05') (7782) terminated with exit code None

# Fixed code
def _change_state_for_tis_without_dagrun(
    self, old_states: List[str], new_state: str, session: Session = None
) -> None:
    """
    For all DAG IDs in the DagBag, look for task instances in the
    old_states and set them to new_state if the corresponding DagRun
    does not exist or exists but is not in the running state. This
    normally should not happen, but it can if the state of DagRuns are
    changed manually.

    :param old_states: examine TaskInstances in this state
    :type old_states: list[airflow.utils.state.State]
    :param new_state: set TaskInstances to this state
    :type new_state: airflow.utils.state.State
    """
    tis_changed = 0
    query = (
        session.query(models.TaskInstance)
        .outerjoin(models.TaskInstance.dag_run)
        .filter(models.TaskInstance.dag_id.in_(list(self.dagbag.dag_ids)))
        .filter(models.TaskInstance.state.in_(old_states))
        .filter(
            or_(
                # pylint: disable=comparison-with-callable
                models.DagRun.state != State.RUNNING,
                # pylint: disable=no-member
                models.DagRun.state.is_(None),
            )
        )
    )
    # We need to do this for mysql as well because it can cause deadlocks
    # as discussed in https://issues.apache.org/jira/browse/AIRFLOW-2516
    if self.using_sqlite or self.using_mysql:
        tis_to_change: List[TI] = with_row_locks(
            query, of=TI, session=session, **skip_locked(session=session)
        ).all()
        for ti in tis_to_change:
            ti.set_state(new_state, session=session)
            tis_changed += 1
    else:
        subq = query.subquery()
        current_time = timezone.utcnow()
        ti_prop_update = {
            models.TaskInstance.state: new_state,
            models.TaskInstance.start_date: current_time,
        }

        # Only add end_date and duration if the new_state is 'success', 'failed' or 'skipped'
        if new_state in State.finished:
            ti_prop_update.update(
                {
                    models.TaskInstance.end_date: current_time,
                    models.TaskInstance.duration: 0,
                }
            )

        tis_changed = (
            session.query(models.TaskInstance)
            .filter(
                models.TaskInstance.dag_id == subq.c.dag_id,
                models.TaskInstance.task_id == subq.c.task_id,
                models.TaskInstance.execution_date == subq.c.execution_date,
            )
            .update(ti_prop_update, synchronize_session=False)
        )

    if tis_changed > 0:
        session.flush()
        self.log.warning(
            "Set %s task instances to state=%s as their associated DagRun was not in RUNNING state",
            tis_changed,
            new_state,
        )
        Stats.gauge("scheduler.tasks.without_dagrun", tis_changed)