pierrejeambrun commented on code in PR #51850:
URL: https://github.com/apache/airflow/pull/51850#discussion_r2266927819


##########
airflow-core/src/airflow/api_fastapi/core_api/services/public/task_instances.py:
##########
@@ -262,21 +262,74 @@ def handle_bulk_delete(
         self, action: BulkDeleteAction[BulkTaskInstanceBody], results: 
BulkActionResponse
     ) -> None:
         """Bulk delete task instances."""
-        to_delete_task_keys = set((task_id, -1) for task_id in action.entities)
-        _, matched_task_keys, not_found_task_keys = 
self.categorize_task_instances(to_delete_task_keys)
-        not_found_task_ids = [task_id for task_id, _ in not_found_task_keys]
+        delete_all_map_indexes: set[str] = set()
+        delete_specific_task_keys: set[tuple[str, int]] = set()
+
+        for entity in action.entities:
+            if isinstance(entity, str):
+                # String task ID - targets the non-mapped task instance 
(map_index = -1 in DB)
+                delete_specific_task_keys.add((entity, -1))
+            else:
+                # BulkTaskInstanceBody object
+                task_id = entity.task_id
+                if entity.map_index == -1:
+                    delete_all_map_indexes.add(task_id)
+                else:
+                    db_map_index = -1 if entity.map_index is None else 
entity.map_index
+                    delete_specific_task_keys.add((task_id, db_map_index))

Review Comment:
   What is the current behavior when we specify a `str`? I would say that if we 
pass a task_id as string, we remove all tis from that.
   
   If we pass a `BulkTaskInstanceBody`, there are two solutions:
   - map_index is None -> we delete all map index matching
   - map_index is defined -> we delete only that map index



##########
airflow-core/src/airflow/api_fastapi/core_api/services/public/task_instances.py:
##########
@@ -262,21 +262,74 @@ def handle_bulk_delete(
         self, action: BulkDeleteAction[BulkTaskInstanceBody], results: 
BulkActionResponse
     ) -> None:
         """Bulk delete task instances."""
-        to_delete_task_keys = set((task_id, -1) for task_id in action.entities)
-        _, matched_task_keys, not_found_task_keys = 
self.categorize_task_instances(to_delete_task_keys)
-        not_found_task_ids = [task_id for task_id, _ in not_found_task_keys]
+        delete_all_map_indexes: set[str] = set()
+        delete_specific_task_keys: set[tuple[str, int]] = set()
+
+        for entity in action.entities:
+            if isinstance(entity, str):
+                # String task ID - targets the non-mapped task instance 
(map_index = -1 in DB)
+                delete_specific_task_keys.add((entity, -1))
+            else:
+                # BulkTaskInstanceBody object
+                task_id = entity.task_id
+                if entity.map_index == -1:
+                    delete_all_map_indexes.add(task_id)
+                else:
+                    db_map_index = -1 if entity.map_index is None else 
entity.map_index
+                    delete_specific_task_keys.add((task_id, db_map_index))
 
         try:
-            if action.action_on_non_existence == BulkActionNotOnExistence.FAIL 
and not_found_task_keys:
-                raise HTTPException(
-                    status_code=status.HTTP_404_NOT_FOUND,
-                    detail=f"The task instances with these task_ids: 
{not_found_task_ids} were not found",
+            # Handle deletion of specific (task_id, map_index) pairs
+            if delete_specific_task_keys:
+                _, matched_task_keys, not_found_task_keys = 
self.categorize_task_instances(
+                    delete_specific_task_keys
                 )
-
-            for task_id, _ in matched_task_keys:
-                existing_task_instance = 
self.session.scalar(select(TI).where(TI.task_id == task_id).limit(1))
-                if existing_task_instance:
-                    self.session.delete(existing_task_instance)
+                not_found_task_ids = [
+                    f"{task_id}" if map_index == -1 else 
f"{task_id}[{map_index}]"
+                    for task_id, map_index in not_found_task_keys

Review Comment:
   For 'specific_task_keys` it means we are targeting a single map index (-1, 
1, 0, etc...), I think we should always show the `map_index` targetted. 
   ```
   f"{task_id}[{map_index}]"
   ```



##########
airflow-core/src/airflow/api_fastapi/core_api/services/public/task_instances.py:
##########
@@ -262,21 +262,74 @@ def handle_bulk_delete(
         self, action: BulkDeleteAction[BulkTaskInstanceBody], results: 
BulkActionResponse
     ) -> None:
         """Bulk delete task instances."""
-        to_delete_task_keys = set((task_id, -1) for task_id in action.entities)
-        _, matched_task_keys, not_found_task_keys = 
self.categorize_task_instances(to_delete_task_keys)
-        not_found_task_ids = [task_id for task_id, _ in not_found_task_keys]
+        delete_all_map_indexes: set[str] = set()
+        delete_specific_task_keys: set[tuple[str, int]] = set()
+
+        for entity in action.entities:
+            if isinstance(entity, str):
+                # String task ID - targets the non-mapped task instance 
(map_index = -1 in DB)
+                delete_specific_task_keys.add((entity, -1))
+            else:
+                # BulkTaskInstanceBody object
+                task_id = entity.task_id
+                if entity.map_index == -1:
+                    delete_all_map_indexes.add(task_id)
+                else:
+                    db_map_index = -1 if entity.map_index is None else 
entity.map_index
+                    delete_specific_task_keys.add((task_id, db_map_index))
 
         try:
-            if action.action_on_non_existence == BulkActionNotOnExistence.FAIL 
and not_found_task_keys:
-                raise HTTPException(
-                    status_code=status.HTTP_404_NOT_FOUND,
-                    detail=f"The task instances with these task_ids: 
{not_found_task_ids} were not found",
+            # Handle deletion of specific (task_id, map_index) pairs
+            if delete_specific_task_keys:
+                _, matched_task_keys, not_found_task_keys = 
self.categorize_task_instances(
+                    delete_specific_task_keys
                 )
-
-            for task_id, _ in matched_task_keys:
-                existing_task_instance = 
self.session.scalar(select(TI).where(TI.task_id == task_id).limit(1))
-                if existing_task_instance:
-                    self.session.delete(existing_task_instance)
+                not_found_task_ids = [
+                    f"{task_id}" if map_index == -1 else 
f"{task_id}[{map_index}]"
+                    for task_id, map_index in not_found_task_keys
+                ]
+
+                if action.action_on_non_existence == 
BulkActionNotOnExistence.FAIL and not_found_task_keys:
+                    raise HTTPException(
+                        status_code=status.HTTP_404_NOT_FOUND,
+                        detail=f"The task instances with these task_ids: 
{not_found_task_ids} were not found",
+                    )
+
+                for task_id, map_index in matched_task_keys:
+                    existing_task_instance = self.session.scalar(
+                        select(TI)
+                        .where(
+                            TI.task_id == task_id,
+                            TI.dag_id == self.dag_id,
+                            TI.run_id == self.dag_run_id,
+                            TI.map_index == map_index,
+                        )
+                        .limit(1)

Review Comment:
   You should only have one `.one_or_none()` if there is more we will have an 
unhandled error, but that should never happens, if there is no result we will 
get `None`



##########
airflow-core/src/airflow/api_fastapi/core_api/services/public/task_instances.py:
##########
@@ -262,21 +262,74 @@ def handle_bulk_delete(
         self, action: BulkDeleteAction[BulkTaskInstanceBody], results: 
BulkActionResponse
     ) -> None:
         """Bulk delete task instances."""
-        to_delete_task_keys = set((task_id, -1) for task_id in action.entities)
-        _, matched_task_keys, not_found_task_keys = 
self.categorize_task_instances(to_delete_task_keys)
-        not_found_task_ids = [task_id for task_id, _ in not_found_task_keys]
+        delete_all_map_indexes: set[str] = set()
+        delete_specific_task_keys: set[tuple[str, int]] = set()
+
+        for entity in action.entities:
+            if isinstance(entity, str):
+                # String task ID - targets the non-mapped task instance 
(map_index = -1 in DB)
+                delete_specific_task_keys.add((entity, -1))
+            else:
+                # BulkTaskInstanceBody object
+                task_id = entity.task_id
+                if entity.map_index == -1:
+                    delete_all_map_indexes.add(task_id)
+                else:
+                    db_map_index = -1 if entity.map_index is None else 
entity.map_index
+                    delete_specific_task_keys.add((task_id, db_map_index))

Review Comment:
   For `BulkTaskInstanceBody` we're free to do what we want because this is new.
   
   For `str` we need to be backward compatible with existing behavior, this is 
a public API.



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