Copilot commented on code in PR #71967:
URL: https://github.com/apache/airflow/pull/71967#discussion_r3857042924


##########
airflow-core/src/airflow/models/dagrun.py:
##########
@@ -1702,7 +1705,21 @@ def _expand_mapped_task_if_needed(ti: TI) -> 
Iterable[TI] | None:
             if schedulable.map_index < 0:
                 new_tis = _expand_mapped_task_if_needed(schedulable)
                 if new_tis is not None:
-                    additional_tis.extend(new_tis)
+                    expanded_tis = list(new_tis)
+                    # Avoid evaluating a huge number of newly expanded TIs in 
the same pass.
+                    # They are persisted already and picked up in subsequent 
loops.
+                    remaining_budget = max(max_tis_per_query - 
len(additional_tis), 0)
+                    if remaining_budget:
+                        additional_tis.extend(expanded_tis[:remaining_budget])

Review Comment:
   The budget only applies while the expansion is created. On the next call, 
`task_instance_scheduling_decisions()` reloads every persisted `None`-state TI 
and `_get_ready_tis` dependency-checks the entire set without this cap; the 
added test even expects all four TIs on its second call. For a 10,000-way map 
this therefore postpones the same 10,000-TI spike by one heartbeat instead of 
smoothing it. Apply a deterministic per-pass budget to the full schedulable set 
(including already-persisted and revised mapped TIs), then test that successive 
passes remain bounded.
   
   ---
   Drafted-by: GitHub Copilot (no human review before posting)



##########
airflow-core/tests/unit/models/test_mappedoperator.py:
##########
@@ -501,6 +501,47 @@ def 
test_expand_mapped_task_task_instance_mutation_hook(dag_maker, session, crea
             assert call.args[0].map_index == expected_map_index[index]
 
 
+def 
test_expand_mapped_task_uses_bulk_insert_when_mutation_hook_is_noop(dag_maker, 
session) -> None:
+    with dag_maker(session=session, serialized=True) as dag:
+        task1 = BaseOperator(task_id="op1")
+        mapped = 
MockOperator.partial(task_id="task_2").expand(arg2=task1.output)
+
+    dr = dag_maker.create_dagrun()
+
+    class NoopHook:
+        is_noop = True
+
+        def __call__(self, *_, **__):
+            return None
+
+    noop_hook = NoopHook()
+
+    with (
+        mock.patch("airflow.settings.task_instance_mutation_hook", noop_hook),
+        mock.patch.object(session, "bulk_insert_mappings", 
wraps=session.bulk_insert_mappings) as bulk_insert,

Review Comment:
   This newly added mock has neither `spec` nor `autospec`, so it can accept 
calls or attributes that the real session method would reject. Please give the 
spy a spec while retaining `wraps`.
   
   ---
   Drafted-by: GitHub Copilot (no human review before posting)



##########
airflow-core/src/airflow/models/taskmap.py:
##########
@@ -256,22 +257,63 @@ def expand_mapped_task(
                 )
             )
 
-        new_tis: list[TaskInstance] = []
-        for index in indexes_to_map:
-            ti = TaskInstance(
-                task,
-                run_id=run_id,
-                map_index=index,
-                state=state,
-                dag_version_id=dag_version_id,
-            )
-            task.log.debug("Expanding TIs upserted %s", ti)
-            _add_and_prime_mapped_ti(
-                ti, task, dr, session=session, 
context_carrier=new_task_run_carrier(dr.context_carrier)
+        from airflow.settings import get_policy_plugin_manager, 
task_instance_mutation_hook
+
+        policy_hook = 
get_policy_plugin_manager().hook.task_instance_mutation_hook
+        hook_is_noop = False
+        if not isinstance(policy_hook, Mock):
+            hook_is_noop = getattr(task_instance_mutation_hook, "is_noop", 
False) is True and all(
+                getattr(hook.function, "is_noop", False) is True for hook in 
policy_hook.get_hookimpls()

Review Comment:
   This production path changes behavior based on whether the Pluggy hook 
object is a `unittest.mock.Mock`, leaking test implementation details into 
runtime logic and requiring a testing module import in `taskmap.py`. Make the 
centrally maintained no-op marker/hook contract authoritative, and have tests 
explicitly configure that marker when replacing a hook rather than adding a 
Mock-specific branch here.
   
   ---
   Drafted-by: GitHub Copilot (no human review before posting)



##########
airflow-core/src/airflow/models/dagrun.py:
##########
@@ -1685,6 +1685,9 @@ def _expand_mapped_task_if_needed(ti: TI) -> Iterable[TI] 
| None:
         expansion_happened = False
         # Set of task ids for which was already done 
_revise_map_indexes_if_mapped
         revised_map_index_task_ids: set[str] = set()
+        max_tis_per_query = airflow_conf.getint("scheduler", 
"max_tis_per_query")
+        if max_tis_per_query <= 0:
+            max_tis_per_query = airflow_conf.getint("core", "parallelism")

Review Comment:
   The newly introduced `max_tis_per_query <= 0` fallback is not exercised by 
the added test, which only sets the value to `2`. Add a case with a 
non-positive query limit and a small `[core] parallelism` value so a regression 
that removes or misreads the fallback fails the test.
   
   ---
   Drafted-by: GitHub Copilot (no human review before posting)



##########
airflow-core/src/airflow/models/dagrun.py:
##########
@@ -2145,17 +2162,50 @@ def _revise_map_indexes_if_mapped(
             )
             session.flush()
 
-        new_tis: list[TI] = []
-        for index in range(total_length):
-            if index in existing_indexes:
-                continue
+        from airflow.settings import task_instance_mutation_hook
+
+        new_indexes = [index for index in range(total_length) if index not in 
existing_indexes]
+        if not new_indexes:
+            return []
+
+        hook_is_noop = getattr(task_instance_mutation_hook, "is_noop", False) 
is True
+        if hook_is_noop:
+            ti_mappings = [
+                TI.insert_mapping(
+                    self.run_id,

Review Comment:
   The new bulk branch in `_revise_map_indexes_if_mapped` has no regression 
test that distinguishes it from the previous per-TI path. The existing 
mapped-length test only checks resulting indexes and would still pass if this 
branch were reverted. Add tests that verify the noop hook uses 
`bulk_insert_mappings` and that a non-noop mutation hook is still invoked and 
persisted for every newly revised index.
   
   ---
   Drafted-by: GitHub Copilot (no human review before posting)



-- 
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