ephraimbuddy commented on code in PR #69832:
URL: https://github.com/apache/airflow/pull/69832#discussion_r3572475313


##########
airflow-core/src/airflow/api_fastapi/core_api/routes/ui/grid.py:
##########
@@ -193,29 +193,41 @@ def get_dag_structure(
     _merge_node_dicts(merged_nodes, nodes)
     del latest_dag
 
-    # Process serdags one by one and merge immediately to reduce memory usage.
-    # Use yield_per() for streaming results and expunge each serdag after 
processing
-    # to allow garbage collection and prevent memory buildup in the session 
identity map.
-    serdags_query = (
-        select(SerializedDagModel)
-        .where(
-            # Even though dag_id is filtered in base_query,
-            # adding this line here can improve the performance of this 
endpoint
-            SerializedDagModel.dag_id == dag_id,
-            SerializedDagModel.id != latest_serdag_id,
-            SerializedDagModel.dag_version_id.in_(
-                select(TaskInstance.dag_version_id)
-                .join(TaskInstance.dag_run)
-                .where(
-                    DagRun.id.in_(run_ids),
-                )
-                .distinct()
-            ),
-        )
-        .execution_options(yield_per=5)  # balance between peak memory usage 
and round trips
+    # Fetch the historical serialized Dags for the visible runs, then detach 
them
+    # and release the DB connection *before* the CPU-bound deserialization + 
merge
+    # below. Deserializing (``serdag.dag``) and merging a large DAG's task 
groups is
+    # expensive; doing it while a server-side cursor (``yield_per``) is open 
keeps the
+    # transaction — and, under PgBouncer transaction pooling, the pooled server
+    # connection — pinned for the entire render. At scale that exhausts the 
pool and
+    # starves task-instance heartbeats, so the connection must not be held 
across the
+    # deserialization. See https://github.com/apache/airflow/issues/65712.
+    #
+    # ``SerializedDagModel.dag`` only reads the already-loaded ``data`` 
column, so it
+    # works on detached instances after the session is released. The result 
set is
+    # bounded by this endpoint's page of runs (one DAG's versions), so 
materializing
+    # it is a safe trade for not holding the connection open.
+    serdags_query = select(SerializedDagModel).where(
+        # Even though dag_id is filtered in base_query,
+        # adding this line here can improve the performance of this endpoint
+        SerializedDagModel.dag_id == dag_id,
+        SerializedDagModel.id != latest_serdag_id,
+        SerializedDagModel.dag_version_id.in_(
+            select(TaskInstance.dag_version_id)
+            .join(TaskInstance.dag_run)
+            .where(
+                DagRun.id.in_(run_ids),
+            )
+            .distinct()
+        ),

Review Comment:
   The previous code used yield_per=5 to control memory growth but this now 
loads everything in memory. I wonder if it's better?



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