perf: batch-load messages in advanced-chat workflow run list to remove N+1 (#38359)

Co-authored-by: Yunlu Wen <yunlu.wen@dify.ai>
This commit is contained in:
mengnanjiugaipeifense
2026-07-08 13:32:32 +00:00
committed by GitHub
co-authored by Yunlu Wen
parent 98d9b11f7b
commit eb3362e646
2 changed files with 63 additions and 9 deletions
+24 -3
View File
@@ -2,7 +2,7 @@ import threading
from collections.abc import Sequence
from typing import TypedDict
from sqlalchemy import Engine
from sqlalchemy import Engine, select
from sqlalchemy.orm import sessionmaker
import contexts
@@ -12,6 +12,7 @@ from models import (
Account,
App,
EndUser,
Message,
WorkflowNodeExecutionModel,
WorkflowRun,
WorkflowRunTriggeredFrom,
@@ -72,9 +73,29 @@ class WorkflowRunService:
pagination = self.get_paginate_workflow_runs(app_model, args, triggered_from)
# Batch-load the associated Message for every run in a single query to avoid
# an N+1 pattern: the deprecated WorkflowRun.message property issues one query
# per run. The filter matches that property exactly (app_id + workflow_run_id).
workflow_runs = pagination.data
run_ids = [workflow_run.id for workflow_run in workflow_runs]
messages_by_run_id: dict[str, Message] = {}
if run_ids:
messages = db.session.scalars(
select(Message).where(
Message.app_id == app_model.id,
Message.workflow_run_id.in_(run_ids),
)
).all()
for loaded_message in messages:
run_id = loaded_message.workflow_run_id
if run_id is None:
continue
# setdefault mirrors scalar()'s single-row-per-run semantics.
messages_by_run_id.setdefault(run_id, loaded_message)
with_message_workflow_runs = []
for workflow_run in pagination.data:
message = workflow_run.message
for workflow_run in workflow_runs:
message = messages_by_run_id.get(workflow_run.id)
with_message_workflow_run = WorkflowWithMessage(workflow_run=workflow_run)
if message:
with_message_workflow_run.message_id = message.id