Eason09053360 opened a new pull request, #73154:
URL: https://github.com/apache/airflow/pull/73154

   ## Why
   
   `DagBundlesManager.sync_bundles_to_db` runs on every Dag processor start, 
and resolved bundle ownership one bundle at a time:
   
   - each configured bundle with a `team_name` issued its own `SELECT ... FROM 
team WHERE name = ?`, repeating the query even when several bundles share a 
team;
   - building `bundle_to_team` touches `DagBundleModel.teams`, which is a 
default `lazy="select"` relationship, so each *stored* bundle lazy-loaded its 
team in a separate round-trip.
   
   Both scale with the number of configured bundles. The `stored` query a few 
lines above already reads all of its rows in one go, so the same function was 
inconsistent with itself.
   
   Measured on SQLite with 4 bundles sharing 2 teams, counting statements that 
read `team`:
   
   | | before | after |
   | --- | --- | --- |
   | first sync (empty `dag_bundle`) | 4 | 1 |
   | subsequent syncs (bundles stored) | 8 | 2 |
   
   The count is now independent of the bundle count rather than proportional to 
it.
   
   ## What
   
   `airflow-core/src/airflow/dag_processing/bundles/manager.py`:
   
   - eager-load `DagBundleModel.teams` with `selectinload` on the `stored` 
query, so `bundle_to_team` reads an already-populated relationship;
   - collect the configured team names into a set and fetch them with a single 
`Team.name.in_(...)`, then resolve each bundle's team from that dict. 
`Team.name` is the primary key, so the dict lookup is equivalent to the 
previous `one_or_none()`. The query is skipped entirely when no bundle declares 
a team, which is the case for every deployment that does not enable `[core] 
multi_team`.
   
   The "team does not exist" check stays inside the loop and keeps its position 
relative to `_extract_and_sign_template`, so error behaviour is unchanged.
   
   `airflow-core/tests/unit/dag_processing/bundles/test_dag_bundle_manager.py` 
adds `test_sync_bundles_to_db_looks_up_teams_in_a_fixed_number_of_queries`, 
which counts `team` statements over two consecutive syncs — the second one 
covers the steady state where bundles are already stored, which is where the 
lazy-load path is reachable. The module's `clear_db` fixture now also clears 
`team`.
   
   ---
   
   ### One question for reviewers
   
   `team.name` is a plain `String`, not a `StringID`, so it does not get the 
`utf8mb3_bin` collation that `airflow.models.base` applies to id columns. On 
MySQL the old `WHERE name = ?` therefore matched case-insensitively, while the 
new dict lookup is exact. A bundle config carrying `"team_name": "Team-A"` 
against a stored `team-a` would have synced on MySQL and now raises.
   
   I did not add a case-folding fallback, because that spelling is already 
rejected everywhere else: `airflow teams create` and `airflow teams sync` both 
hold bundle-config team names to `TEAM_NAME_PATTERN` (lower case only), and 
migration `0131_3_4_0_lower_case_team_names` folds stored names to lower case. 
Keeping the leniency would make this function accept a name that `teams sync` 
refuses. Happy to add the fallback if you would rather not change that at all.
   
   ---
   
   ##### Was generative AI tooling used to co-author this PR?
   
   - [X] Yes — Claude Code (Opus 5)
   
   Generated-by: Claude Code (Opus 5) following [the 
guidelines](https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#gen-ai-assisted-contributions)
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to