# Buggy code
def bulk_write_to_db(cls, dags: Collection["DAG"], session=None):
    """
    Ensure the DagModel rows for the given dags are up-to-date in the dag table in the DB, including
    calculated fields.

    Note that this method can be called for both DAGs and SubDAGs. A SubDag is actually a SubDagOperator.

    :param dags: the DAG objects to save to the DB
    :type dags: List[airflow.models.dag.DAG]
    :return: None
    """
    if not dags:
        return

    log.info("Sync %s DAGs", len(dags))
    dag_by_ids = {dag.dag_id: dag for dag in dags}
    dag_ids = set(dag_by_ids.keys())
    query = (
        session.query(DagModel)
        .options(joinedload(DagModel.tags, innerjoin=False))
        .filter(DagModel.dag_id.in_(dag_ids))
    )
    orm_dags = with_row_locks(query, of=DagModel).all()

    existing_dag_ids = {orm_dag.dag_id for orm_dag in orm_dags}
    missing_dag_ids = dag_ids.difference(existing_dag_ids)

    for missing_dag_id in missing_dag_ids:
        orm_dag = DagModel(dag_id=missing_dag_id)
        dag = dag_by_ids[missing_dag_id]
        if dag.is_paused_upon_creation is not None:
            orm_dag.is_paused = dag.is_paused_upon_creation
        orm_dag.tags = []
        log.info("Creating ORM DAG for %s", dag.dag_id)
        session.add(orm_dag)
        orm_dags.append(orm_dag)

    # Get the latest dag run for each existing dag as a single query (avoid n+1 query)
    most_recent_dag_runs = dict(
        session.query(DagRun.dag_id, func.max_(DagRun.execution_date))
        .filter(
            DagRun.dag_id.in_(existing_dag_ids),
            or_(
                DagRun.run_type == DagRunType.BACKFILL_JOB,
                DagRun.run_type == DagRunType.SCHEDULED,
            ),
        )
        .group_by(DagRun.dag_id)
        .all()
    )

    # Get number of active dagruns for all dags we are processing as a single query.
    num_active_runs = dict(
        session.query(DagRun.dag_id, func.count("*"))
        .filter(
            DagRun.dag_id.in_(existing_dag_ids),
            DagRun.state == State.RUNNING,  # pylint: disable=comparison-with-callable
            DagRun.external_trigger.is_(False),
        )
        .group_by(DagRun.dag_id)
        .all()
    )

    for orm_dag in sorted(orm_dags, key=lambda d: d.dag_id):
        dag = dag_by_ids[orm_dag.dag_id]
        if dag.is_subdag:
            orm_dag.is_subdag = True
            orm_dag.fileloc = dag.parent_dag.fileloc  # type: ignore
            orm_dag.root_dag_id = dag.parent_dag.dag_id  # type: ignore
            orm_dag.owners = dag.parent_dag.owner  # type: ignore
        else:
            orm_dag.is_subdag = False
            orm_dag.fileloc = dag.fileloc
            orm_dag.owners = dag.owner
        orm_dag.is_active = True
        orm_dag.default_view = dag.default_view
        orm_dag.description = dag.description
        orm_dag.schedule_interval = dag.schedule_interval
        orm_dag.concurrency = dag.concurrency
        orm_dag.has_task_concurrency_limits = any(
            t.task_concurrency is not None for t in dag.tasks
        )

        orm_dag.calculate_dagrun_date_fields(
            dag,
            most_recent_dag_runs.get(dag.dag_id),
            num_active_runs.get(dag.dag_id, 0),
        )

        for orm_tag in list(orm_dag.tags):
            if orm_tag.name not in orm_dag.tags:
                session.delete(orm_tag)
            orm_dag.tags.remove(orm_tag)
        if dag.tags:
            orm_tag_names = [t.name for t in orm_dag.tags]
            for dag_tag in list(dag.tags):
                if dag_tag not in orm_tag_names:
                    dag_tag_orm = DagTag(name=dag_tag, dag_id=dag.dag_id)
                    orm_dag.tags.append(dag_tag_orm)
                    session.add(dag_tag_orm)

    if settings.STORE_DAG_CODE:
        DagCode.bulk_sync_to_db([dag.fileloc for dag in orm_dags])

    # Issue SQL/finish "Unit of Work", but let @provide_session commit (or if passed a session, let caller
    # decide when to commit
    session.flush()

    for dag in dags:
        cls.bulk_write_to_db(dag.subdags, session=session)


# 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 bulk_write_to_db(cls, dags: Collection["DAG"], session=None):
    """
    Ensure the DagModel rows for the given dags are up-to-date in the dag table in the DB, including
    calculated fields.

    Note that this method can be called for both DAGs and SubDAGs. A SubDag is actually a SubDagOperator.

    :param dags: the DAG objects to save to the DB
    :type dags: List[airflow.models.dag.DAG]
    :return: None
    """
    if not dags:
        return

    log.info("Sync %s DAGs", len(dags))
    dag_by_ids = {dag.dag_id: dag for dag in dags}
    dag_ids = set(dag_by_ids.keys())
    query = (
        session.query(DagModel)
        .options(joinedload(DagModel.tags, innerjoin=False))
        .filter(DagModel.dag_id.in_(dag_ids))
    )
    orm_dags = with_row_locks(query, of=DagModel, session=session).all()

    existing_dag_ids = {orm_dag.dag_id for orm_dag in orm_dags}
    missing_dag_ids = dag_ids.difference(existing_dag_ids)

    for missing_dag_id in missing_dag_ids:
        orm_dag = DagModel(dag_id=missing_dag_id)
        dag = dag_by_ids[missing_dag_id]
        if dag.is_paused_upon_creation is not None:
            orm_dag.is_paused = dag.is_paused_upon_creation
        orm_dag.tags = []
        log.info("Creating ORM DAG for %s", dag.dag_id)
        session.add(orm_dag)
        orm_dags.append(orm_dag)

    # Get the latest dag run for each existing dag as a single query (avoid n+1 query)
    most_recent_dag_runs = dict(
        session.query(DagRun.dag_id, func.max_(DagRun.execution_date))
        .filter(
            DagRun.dag_id.in_(existing_dag_ids),
            or_(
                DagRun.run_type == DagRunType.BACKFILL_JOB,
                DagRun.run_type == DagRunType.SCHEDULED,
            ),
        )
        .group_by(DagRun.dag_id)
        .all()
    )

    # Get number of active dagruns for all dags we are processing as a single query.
    num_active_runs = dict(
        session.query(DagRun.dag_id, func.count("*"))
        .filter(
            DagRun.dag_id.in_(existing_dag_ids),
            DagRun.state == State.RUNNING,  # pylint: disable=comparison-with-callable
            DagRun.external_trigger.is_(False),
        )
        .group_by(DagRun.dag_id)
        .all()
    )

    for orm_dag in sorted(orm_dags, key=lambda d: d.dag_id):
        dag = dag_by_ids[orm_dag.dag_id]
        if dag.is_subdag:
            orm_dag.is_subdag = True
            orm_dag.fileloc = dag.parent_dag.fileloc  # type: ignore
            orm_dag.root_dag_id = dag.parent_dag.dag_id  # type: ignore
            orm_dag.owners = dag.parent_dag.owner  # type: ignore
        else:
            orm_dag.is_subdag = False
            orm_dag.fileloc = dag.fileloc
            orm_dag.owners = dag.owner
        orm_dag.is_active = True
        orm_dag.default_view = dag.default_view
        orm_dag.description = dag.description
        orm_dag.schedule_interval = dag.schedule_interval
        orm_dag.concurrency = dag.concurrency
        orm_dag.has_task_concurrency_limits = any(
            t.task_concurrency is not None for t in dag.tasks
        )

        orm_dag.calculate_dagrun_date_fields(
            dag,
            most_recent_dag_runs.get(dag.dag_id),
            num_active_runs.get(dag.dag_id, 0),
        )

        for orm_tag in list(orm_dag.tags):
            if orm_tag.name not in orm_dag.tags:
                session.delete(orm_tag)
            orm_dag.tags.remove(orm_tag)
        if dag.tags:
            orm_tag_names = [t.name for t in orm_dag.tags]
            for dag_tag in list(dag.tags):
                if dag_tag not in orm_tag_names:
                    dag_tag_orm = DagTag(name=dag_tag, dag_id=dag.dag_id)
                    orm_dag.tags.append(dag_tag_orm)
                    session.add(dag_tag_orm)

    if settings.STORE_DAG_CODE:
        DagCode.bulk_sync_to_db([dag.fileloc for dag in orm_dags])

    # Issue SQL/finish "Unit of Work", but let @provide_session commit (or if passed a session, let caller
    # decide when to commit
    session.flush()

    for dag in dags:
        cls.bulk_write_to_db(dag.subdags, session=session)