tirkarthi commented on code in PR #61483:
URL: https://github.com/apache/airflow/pull/61483#discussion_r2767333305


##########
airflow-core/src/airflow/api_fastapi/core_api/datamodels/dags.py:
##########
@@ -41,6 +42,19 @@
 if TYPE_CHECKING:
     from airflow.serialization.definitions.param import SerializedParamsDict
 
+
+@lru_cache(maxsize=1)
+def _get_file_token_serializer() -> URLSafeSerializer:

Review Comment:
   Since the secret_key doesn't change could this be just set as a module level 
private attribute instead of a cached function?



##########
airflow-core/src/airflow/api_fastapi/core_api/routes/ui/dags.py:
##########
@@ -234,18 +234,22 @@ def get_dags(
             pending_actions_by_dag_id[dag_id].append(hitl_detail)
 
     # aggregate rows by dag_id
-    dag_runs_by_dag_id: dict[str, DAGWithLatestDagRunsResponse] = {
-        dag.dag_id: DAGWithLatestDagRunsResponse.model_validate(
-            {
-                **DAGResponse.model_validate(dag).model_dump(),
-                "asset_expression": dag.asset_expression,
-                "latest_dag_runs": [],
-                "pending_actions": pending_actions_by_dag_id[dag.dag_id],
-                "is_favorite": dag.dag_id in favorite_dag_ids,
-            }
+    # Performance optimization: Validate ORM to DAGResponse once per DAG, then 
use model_construct
+    # to build DAGWithLatestDagRunsResponse without redundant validation.
+    # The original pattern (model_validate -> model_dump -> model_validate) 
caused triple
+    # serialization overhead that scaled poorly with the number of DAGs.
+    dag_runs_by_dag_id: dict[str, DAGWithLatestDagRunsResponse] = {}
+    for dag in dags:
+        dag_response = DAGResponse.model_validate(dag)
+        dag_data = dag_response.model_dump()
+        dag_data["asset_expression"] = dag.asset_expression
+        dag_data["latest_dag_runs"] = []
+        dag_data["pending_actions"] = pending_actions_by_dag_id[dag.dag_id]
+        dag_data["is_favorite"] = dag.dag_id in favorite_dag_ids
+        dag_runs_by_dag_id[dag.dag_id] = 
DAGWithLatestDagRunsResponse.model_construct(

Review Comment:
   I see below traceback while testing dags with tags. Can you please test this 
PR with dags that have tags? I tried the below patch but it would change the 
signature to include dict.
   
   ```diff
   diff --git 
a/airflow-core/src/airflow/api_fastapi/core_api/datamodels/dags.py 
b/airflow-core/src/airflow/api_fastapi/core_api/datamodels/dags.py
   index cf0d69357a..5516cea9c8 100644
   --- a/airflow-core/src/airflow/api_fastapi/core_api/datamodels/dags.py
   +++ b/airflow-core/src/airflow/api_fastapi/core_api/datamodels/dags.py
   @@ -102,7 +102,7 @@ class DAGResponse(BaseModel):
        @field_serializer("tags")
        def serialize_tags(self, tags: list[DagTagResponse]) -> 
list[DagTagResponse]:
            """Sort tags alphabetically by name."""
   -        return sorted(tags, key=lambda tag: tag.name)
   +        return sorted(tags, key=lambda tag: tag["name"] if isinstance(tag, 
dict) else tag.name)
    
        @field_validator("owners", mode="before")
        @classmethod
   
   ```
   
   ```
   ERROR:    Exception in ASGI application
   Traceback (most recent call last):
     File 
"/home/karthikeyan/stuff/python/airflow/airflow-core/src/airflow/api_fastapi/core_api/datamodels/dags.py",
 line 105, in serialize_tags
       return sorted(tags, key=lambda tag: tag.name)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     File 
"/home/karthikeyan/stuff/python/airflow/airflow-core/src/airflow/api_fastapi/core_api/datamodels/dags.py",
 line 105, in <lambda>
       return sorted(tags, key=lambda tag: tag.name)
                                           ^^^^^^^^
   AttributeError: 'dict' object has no attribute 'name'
   
   The above exception was the direct cause of the following exception:
   
   Traceback (most recent call last):
     File 
"/home/karthikeyan/stuff/python/airflow/.venv/lib/python3.11/site-packages/uvicorn/protocols/http/httptools_impl.py",
 line 409, in run_asgi
       result = await app(  # type: ignore[func-returns-value]
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     File 
"/home/karthikeyan/stuff/python/airflow/.venv/lib/python3.11/site-packages/fastapi/applications.py",
 line 1138, in __call__
       await super().__call__(scope, receive, send)
     File 
"/home/karthikeyan/stuff/python/airflow/.venv/lib/python3.11/site-packages/starlette/applications.py",
 line 112, in __call__
       await self.middleware_stack(scope, receive, send)
     File 
"/home/karthikeyan/stuff/python/airflow/.venv/lib/python3.11/site-packages/starlette/middleware/errors.py",
 line 187, in __call__
       raise exc
     File 
"/home/karthikeyan/stuff/python/airflow/.venv/lib/python3.11/site-packages/starlette/middleware/errors.py",
 line 165, in __call__
       await self.app(scope, receive, _send)
     File 
"/home/karthikeyan/stuff/python/airflow/.venv/lib/python3.11/site-packages/starlette/middleware/cors.py",
 line 85, in __call__
       await self.app(scope, receive, send)
     File 
"/home/karthikeyan/stuff/python/airflow/.venv/lib/python3.11/site-packages/starlette/middleware/gzip.py",
 line 22, in __call__
       await self.app(scope, receive, send)
     File 
"/home/karthikeyan/stuff/python/airflow/.venv/lib/python3.11/site-packages/starlette/middleware/base.py",
 line 177, in __call__
       with recv_stream, send_stream, collapse_excgroups():
     File "/usr/lib/python3.11/contextlib.py", line 158, in __exit__
       self.gen.throw(typ, value, traceback)
     File 
"/home/karthikeyan/stuff/python/airflow/.venv/lib/python3.11/site-packages/starlette/_utils.py",
 line 82, in collapse_excgroups
       raise exc
     File 
"/home/karthikeyan/stuff/python/airflow/.venv/lib/python3.11/site-packages/starlette/middleware/base.py",
 line 179, in __call__
       response = await self.dispatch_func(request, call_next)
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     File 
"/home/karthikeyan/stuff/python/airflow/airflow-core/src/airflow/api_fastapi/auth/middlewares/refresh_token.py",
 line 61, in dispatch
       response = await call_next(request)
                  ^^^^^^^^^^^^^^^^^^^^^^^^
     File 
"/home/karthikeyan/stuff/python/airflow/.venv/lib/python3.11/site-packages/starlette/middleware/base.py",
 line 154, in call_next
       raise app_exc
     File 
"/home/karthikeyan/stuff/python/airflow/.venv/lib/python3.11/site-packages/starlette/middleware/base.py",
 line 141, in coro
       await self.app(scope, receive_or_disconnect, send_no_error)
     File 
"/home/karthikeyan/stuff/python/airflow/.venv/lib/python3.11/site-packages/starlette/middleware/exceptions.py",
 line 62, in __call__
       await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
     File 
"/home/karthikeyan/stuff/python/airflow/.venv/lib/python3.11/site-packages/starlette/_exception_handler.py",
 line 53, in wrapped_app
       raise exc
     File 
"/home/karthikeyan/stuff/python/airflow/.venv/lib/python3.11/site-packages/starlette/_exception_handler.py",
 line 42, in wrapped_app
       await app(scope, receive, sender)
     File 
"/home/karthikeyan/stuff/python/airflow/.venv/lib/python3.11/site-packages/fastapi/middleware/asyncexitstack.py",
 line 18, in __call__
       await self.app(scope, receive, send)
     File 
"/home/karthikeyan/stuff/python/airflow/.venv/lib/python3.11/site-packages/starlette/routing.py",
 line 715, in __call__
       await self.middleware_stack(scope, receive, send)
     File 
"/home/karthikeyan/stuff/python/airflow/.venv/lib/python3.11/site-packages/starlette/routing.py",
 line 735, in app
       await route.handle(scope, receive, send)
     File 
"/home/karthikeyan/stuff/python/airflow/.venv/lib/python3.11/site-packages/starlette/routing.py",
 line 288, in handle
       await self.app(scope, receive, send)
     File 
"/home/karthikeyan/stuff/python/airflow/.venv/lib/python3.11/site-packages/fastapi/routing.py",
 line 115, in app
       await wrap_app_handling_exceptions(app, request)(scope, receive, send)
     File 
"/home/karthikeyan/stuff/python/airflow/.venv/lib/python3.11/site-packages/starlette/_exception_handler.py",
 line 53, in wrapped_app
       raise exc
     File 
"/home/karthikeyan/stuff/python/airflow/.venv/lib/python3.11/site-packages/starlette/_exception_handler.py",
 line 42, in wrapped_app
       await app(scope, receive, sender)
     File 
"/home/karthikeyan/stuff/python/airflow/.venv/lib/python3.11/site-packages/fastapi/routing.py",
 line 101, in app
       response = await f(request)
                  ^^^^^^^^^^^^^^^^
     File 
"/home/karthikeyan/stuff/python/airflow/.venv/lib/python3.11/site-packages/fastapi/routing.py",
 line 377, in app
       content = await serialize_response(
                 ^^^^^^^^^^^^^^^^^^^^^^^^^
     File 
"/home/karthikeyan/stuff/python/airflow/.venv/lib/python3.11/site-packages/fastapi/routing.py",
 line 221, in serialize_response
       return field.serialize(
              ^^^^^^^^^^^^^^^^
     File 
"/home/karthikeyan/stuff/python/airflow/.venv/lib/python3.11/site-packages/fastapi/_compat/v2.py",
 line 197, in serialize
       return self._type_adapter.dump_python(
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     File 
"/home/karthikeyan/stuff/python/airflow/.venv/lib/python3.11/site-packages/pydantic/type_adapter.py",
 line 572, in dump_python
       return self.serializer.to_python(
              ^^^^^^^^^^^^^^^^^^^^^^^^^^
   pydantic_core._pydantic_core.PydanticSerializationError: Error calling 
function `serialize_tags`: AttributeError: 'dict' object has no attribute 'name'
   ```



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