Skip to content

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

Open
xiaweiwei67-stack wants to merge 3 commits into
langgenius:mainfrom
xiaweiwei67-stack:perf/workflow-run-list-batch-messages
Open

perf: batch-load messages in advanced-chat workflow run list to remove N+1#38359
xiaweiwei67-stack wants to merge 3 commits into
langgenius:mainfrom
xiaweiwei67-stack:perf/workflow-run-list-batch-messages

Conversation

@xiaweiwei67-stack

Copy link
Copy Markdown
Contributor

Summary

WorkflowRunService.get_paginate_advanced_chat_workflow_runs (the console "workflow runs" log list for advanced-chat apps) attaches message_id / conversation_id to each run by reading the deprecated WorkflowRun.message property inside the loop. That property runs select(Message).where(Message.app_id == self.app_id, Message.workflow_run_id == self.id) - i.e. one query per run, an N+1 that scales with page size.

Change

Batch-load the messages for the whole page in a single query and look them up in memory:

messages = db.session.scalars(
    select(Message).where(
        Message.app_id == app_model.id,
        Message.workflow_run_id.in_(run_ids),
    )
).all()
  • Filter semantics are identical to the deprecated property (app_id + workflow_run_id); all runs on a page belong to app_model, so Message.app_id == app_model.id matches run.app_id.
  • setdefault keeps one message per run, mirroring scalar()'s single-row behavior.
  • Query count drops from N+1 to a constant (one batched query per page).
  • Zero behavior change; the deprecated WorkflowRun.message property is left untouched for other callers.

Same class as the already-merged #38002 (batch-load MessageFile in TokenBufferMemory).

Tests

Updated test_get_paginate_advanced_chat_workflow_runs_should_attach_message_fields_when_message_exists for the batched path, and added test_get_paginate_advanced_chat_workflow_runs_batch_loads_messages_without_n_plus_one, which asserts db.session.scalars is called exactly once regardless of run count (regression guard against N+1).

…e N+1

WorkflowRunService.get_paginate_advanced_chat_workflow_runs attached
message_id / conversation_id to each run by reading the deprecated
WorkflowRun.message property inside the loop, which issues one
select(Message) query per run - an N+1 that scales with page size on the
console "workflow runs" log list.

Batch-load the messages for the whole page in a single query
(Message.app_id == app_model.id, Message.workflow_run_id.in_(run_ids)) and
look them up in memory, preserving the exact filter semantics. Query count
drops from N+1 to a constant regardless of page size.

Mirrors the same optimization already merged for TokenBufferMemory (langgenius#38002).
@dosubot dosubot Bot added the size:S This PR changes 10-29 lines, ignoring generated files. label Jul 3, 2026
@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Pyrefly Diff

base → PR
--- /tmp/pyrefly_base.txt	2026-07-03 04:05:59.995729363 +0000
+++ /tmp/pyrefly_pr.txt	2026-07-03 04:05:51.707687199 +0000
@@ -7668,9 +7668,9 @@
 ERROR Argument `list[int | str]` is not assignable to parameter `mentioned_user_ids` with type `Sequence[str]` in function `services.workflow_comment_service.WorkflowCommentService._filter_valid_mentioned_user_ids` [bad-argument-type]
   --> tests/unit_tests/services/test_workflow_comment_service.py:49:13
 ERROR Argument `dict[str, str]` is not assignable to parameter `args` with type `WorkflowRunListArgs` in function `services.workflow_run_service.WorkflowRunService.get_paginate_workflow_runs` [bad-argument-type]
-   --> tests/unit_tests/services/test_workflow_run_service.py:102:18
+   --> tests/unit_tests/services/test_workflow_run_service.py:109:18
 ERROR `Literal['2']` is not assignable to TypedDict key `limit` with type `int` [bad-typed-dict-key]
-   --> tests/unit_tests/services/test_workflow_run_service.py:132:103
+   --> tests/unit_tests/services/test_workflow_run_service.py:139:103
 ERROR Argument `dict[str, dict[str, str | dict[str, str]] | str]` is not assignable to parameter `object` with type `dict[str, dict[str, list[Unknown] | str] | str]` in function `list.append` [bad-argument-type]
    --> tests/unit_tests/services/test_workflow_service.py:225:13
 ERROR `dict[@_, @_]` is not assignable to TypedDict key `data` with type `BaseNodeData` [bad-typed-dict-key]

@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Pyrefly Type Coverage

Metric Base PR Delta
Type coverage 52.24% 52.24% +0.00%
Strict coverage 51.75% 51.75% +0.00%
Typed symbols 31,928 31,931 +3
Untyped symbols 29,468 29,468 0
Modules 2963 2963 0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:S This PR changes 10-29 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant