Apache Airflow version
main (3.x)
What happened?
SerializedDagModel.get_count() returns session.scalar(select(func.count()).select_from(cls)) or 0. The or 0 collapses the legitimately-impossible None case with the "table is empty" case (which actually returns 0, not None) — and also masks any transient DB error that surfaces as None from session.scalar().
In practice the only path that reaches or 0 is a DB-side failure, and silently emitting serialized_dag.count = 0 causes false on-call pages ("all DAGs disappeared!") while hiding the real problem.
What you think should happen instead?
Drop the or 0. select(func.count()) on an aggregate is guaranteed to return an int, so a None return is anomalous and should raise (or log warning + raise). Callers can opt into try/except as needed.
Anything else?
This is a 1-line change in the model; one caller in dag_processing/manager.py emits the count to a metric path and should wrap in try/except after the change.
Are you willing to submit PR?
Code of Conduct
Apache Airflow version
main (3.x)
What happened?
SerializedDagModel.get_count()returnssession.scalar(select(func.count()).select_from(cls)) or 0. Theor 0collapses the legitimately-impossible None case with the "table is empty" case (which actually returns 0, not None) — and also masks any transient DB error that surfaces as None fromsession.scalar().In practice the only path that reaches
or 0is a DB-side failure, and silently emittingserialized_dag.count = 0causes false on-call pages ("all DAGs disappeared!") while hiding the real problem.What you think should happen instead?
Drop the
or 0.select(func.count())on an aggregate is guaranteed to return an int, so a None return is anomalous and should raise (or log warning + raise). Callers can opt into try/except as needed.Anything else?
This is a 1-line change in the model; one caller in
dag_processing/manager.pyemits the count to a metric path and should wrap in try/except after the change.Are you willing to submit PR?
Code of Conduct