Skip per-DAG FAB permission sync when access_control is unset (3.2.2) - #4
Open
dor-bernstein wants to merge 1 commit into
Open
Skip per-DAG FAB permission sync when access_control is unset (3.2.2)#4dor-bernstein wants to merge 1 commit into
dor-bernstein wants to merge 1 commit into
Conversation
_serialize_dag_capturing_errors() calls _sync_dag_perms() for every DAG on every parse when the FAB auth manager is active. sync_perm_for_dag() creates a per-DAG `DAG:<dag_id>` resource (ab_view_menu / ab_permission_view) inside the same transaction that holds SELECT ... FOR UPDATE locks on the dag/dag_run rows (DagModelOperation.find_orm_dags -> update_dag_parsing_results_in_db). With a large number of dynamically-generated DAGs this per-DAG sync dominates the parse transaction's lock-hold time (observed: a single dag-processor transaction holding dag-row locks for ~3-4 minutes). Since 3.2.0 the scheduler writes `exceeds_max_non_backfill` to `dag` rows inside its critical section and blocks behind that lock, so the scheduler stalls (critical_section duration up, CPU down) whenever a parse is in flight. When a DAG defines no explicit access_control, the per-DAG resources are unused because DAG-level access is granted through the global DAG resource on the role. Gate the sync on `dag.access_control` so it only runs when there is per-DAG authz to sync. No authorization behaviour changes for DAGs without access_control. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
After upgrading Airflow 3.1.7 → 3.2.2, scheduling became severely slow (mainly visible as Celery tasks not getting queued). Metrics showed the classic signature of the scheduler being blocked, not busy:
scheduler.critical_section_durationup, scheduler CPU down.Live investigation on the metadata DB (Postgres) found
dag-table row-lock contention:idle in transaction/activeonSELECT dag_code…) that has takenSELECT … FOR UPDATElocks on thedag/dag_runrows (DagModelOperation.find_orm_dags).UPDATE dag SET exceeds_max_non_backfill=… WHERE dag.dag_id = …, waiting ontransactionidlocks held by that dag-processor transaction.Two things make this new in 3.2:
exceeds_max_non_backfilltodagrows inside its critical section (added in Separate "next dag run" from "max active runs" apache/airflow#60006, shipped 3.2.0). In 3.1 it computed this in memory and never took adag-row write lock there, so it never collided with the dag-processor's locks._serialize_dag_capturing_errors()calls_sync_dag_perms()for every DAG on every parse (FAB auth manager), andsync_perm_for_dag()creates/refreshes a per-DAGDAG:<dag_id>resource (ab_view_menu/ab_permission_view) inside that same transaction — we caught the blocker running exactly theseSELECT ab_view_menu …statements.This is amplified enormously by our workload: thousands of dynamically-generated per-tenant DAGs, so one parse transaction locks a huge number of
dagrows for minutes.Change
Gate the per-DAG permission sync on the DAG actually defining
access_control:When a DAG has no explicit
access_control, its per-DAG FAB resources are unused — DAG-level access is granted via the global DAG resource on the role. Skipping the sync in that case removes a major component of the parse transaction's lock-hold work with no change to authorization.Why this is safe for us
access_control(verified).OktaTeamAuthorizergroup→role mapping (authentication + role assignment). This change only touches per-DAG resource creation (authorization granularity we don't use).Admin/Viewerroles, which grant DAG access via the global DAG resource, not per-DAG grants.Scope / what this deliberately does NOT do
This does not chunk the
update_dags/update_dag_parsing_results_in_dbtransaction. That transaction's scope is per parsed file, so the deeper fix for the lock-hold duration is to reduce DAGs-per-file in our dynamic DAG factory (nitro side) and/or a properly test-validated core change to batch-commitbulk_write_to_db. Both are follow-ups; this PR is the low-risk, high-confidence first step that removes the FAB perm-sync component we directly observed blocking the scheduler.Testing
airflow-coredag-processing / permission tests in CI.daglocks for minutes and thatUPDATE dag SET exceeds_max_non_backfillno longer blocks.References
exceeds_max_non_backfill(3.2.0), the new schedulerdag-row write.daglocks), different function (deactivate_*); first shipped in 3.3.0, does not address this path.max_active_runs.🤖 Generated with Claude Code