This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch v3-3-test
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/v3-3-test by this push:
     new bb8198f6cab [v3-3-test] Speed up Trigger.clean_unused query to prevent 
Triggerer crashes  (#68244) (#70668)
bb8198f6cab is described below

commit bb8198f6cab5b3d2c971c4b0c77cd0724ca193c0
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Thu Jul 30 18:50:51 2026 +0200

    [v3-3-test] Speed up Trigger.clean_unused query to prevent Triggerer 
crashes  (#68244) (#70668)
    
    * Use NOT EXISTS anti-join in Trigger.clean_unused instead of LEFT JOIN + 
aggregate
    
    * add FOR UPDATE SKIP LOCKED to Trigger.clean_unused to avoid deadlocks 
between concurrent triggerer pods
    
    * use with_row_locks helper and scope lock to trigger table
    (cherry picked from commit 2c1035a973227a1dd5187429917ff2d60afee774)
    
    Co-authored-by: AntonioBergonzi 
<[email protected]>
    Co-authored-by: Rahul Vats <[email protected]>
---
 airflow-core/src/airflow/models/trigger.py | 11 +++++------
 1 file changed, 5 insertions(+), 6 deletions(-)

diff --git a/airflow-core/src/airflow/models/trigger.py 
b/airflow-core/src/airflow/models/trigger.py
index 14473f36fda..a7e7b8ee293 100644
--- a/airflow-core/src/airflow/models/trigger.py
+++ b/airflow-core/src/airflow/models/trigger.py
@@ -250,13 +250,12 @@ class Trigger(Base):
                 )
 
         # Get all triggers that have no task instances, assets, or callbacks 
depending on them and delete them
-        ids = (
-            select(cls.id)
-            .where(~cls.assets.any(), ~cls.callback.has())
-            .join(TaskInstance, cls.id == TaskInstance.trigger_id, 
isouter=True)
-            .group_by(cls.id)
-            .having(func.count(TaskInstance.trigger_id) == 0)
+        ids = select(cls.id).where(
+            ~cls.assets.any(),
+            ~cls.callback.has(),
+            ~cls.task_instance.has(),
         )
+        ids = with_row_locks(ids, session, of=cls, skip_locked=True, 
key_share=False)
         if get_dialect_name(session) == "mysql":
             # MySQL doesn't support DELETE with JOIN, so we need to do it in 
two steps
             ids_list = list(session.scalars(ids).all())

Reply via email to