From 985904b95e78ad94cc928e812c47aa212dabebb4 Mon Sep 17 00:00:00 2001 From: oudecheng <13802883547@139.com> Date: Tue, 18 Aug 2026 23:06:19 +0800 Subject: [PATCH] =?UTF-8?q?perf(topics):=20=E6=96=B0=E5=BB=BA/=E5=88=A0?= =?UTF-8?q?=E9=99=A4/=E9=87=8D=E5=91=BD=E5=90=8D=E8=AF=9D=E9=A2=98?= =?UTF-8?q?=E5=90=8C=E6=A0=B7=E8=B5=B0=20spawn=5Fblocking=20=E7=BB=9F?= =?UTF-8?q?=E4=B8=80=E6=8F=90=E9=80=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 抽取 list_topic_summaries_blocking 公共 helper(list_topics + build_topic_summaries 在 blocking 线程池执行) - create/delete/rename 三个话题命令返回侧边栏刷新列表时统一走 helper,DB 查询不再阻塞 tokio worker - 结合增量统计列,四个话题命令的列表构建均从 O(全部消息) 降为 O(话题数) --- src/command/handlers/delete_topic.rs | 13 +++++----- src/command/handlers/list_topics.rs | 38 ++++++++++++++++++---------- src/command/handlers/rename_topic.rs | 24 +++++++++--------- src/command/handlers/session.rs | 13 +++++----- 4 files changed, 48 insertions(+), 40 deletions(-) diff --git a/src/command/handlers/delete_topic.rs b/src/command/handlers/delete_topic.rs index 6000fae..6a07b02 100644 --- a/src/command/handlers/delete_topic.rs +++ b/src/command/handlers/delete_topic.rs @@ -1,7 +1,6 @@ use crate::command::Command; use crate::command::context::CommandContext; use crate::command::handler::{CommandHandler, CommandMetadata}; -use crate::command::handlers::list_topics::build_topic_summaries; use crate::command::response::{CommandError, CommandResponse, MessageKind}; use crate::gateway::session::SessionManager; use crate::storage::SessionStore; @@ -82,12 +81,12 @@ async fn handle_delete_topic( .map_err(|e| CommandError::new("DELETE_TOPIC_ERROR", e.to_string()))?; // 查询更新后的话题列表,返回给前端刷新侧边栏 - let topics = handler - .store - .list_topics(session_id) - .map_err(|e| CommandError::new("LIST_TOPICS_ERROR", e.to_string()))?; - - let topic_summaries = build_topic_summaries(handler.store.as_ref(), topics)?; + let topic_summaries = + crate::command::handlers::list_topics::list_topic_summaries_blocking( + handler.store.clone(), + session_id, + ) + .await?; let topics_json = serde_json::to_string(&topic_summaries) .map_err(|e| CommandError::new("SERIALIZE_ERROR", e.to_string()))?; diff --git a/src/command/handlers/list_topics.rs b/src/command/handlers/list_topics.rs index 4f66c55..7fefd80 100644 --- a/src/command/handlers/list_topics.rs +++ b/src/command/handlers/list_topics.rs @@ -97,6 +97,28 @@ pub fn build_topic_summaries( Ok(summaries) } +/// 在 blocking 线程池中执行 list_topics + build_topic_summaries。 +/// +/// 同步 rusqlite 查询不得直接跑在 tokio worker 上,否则大库查询会饿死 +/// 同运行时上的其他任务。list / create / delete / rename 四个话题命令 +/// 都返回完整的 TopicSummary 列表供前端刷新侧边栏,统一走本 helper。 +pub async fn list_topic_summaries_blocking( + store: Arc, + session_id: &str, +) -> Result, CommandError> { + let session_id_bg = session_id.to_string(); + tokio::task::spawn_blocking( + move || -> Result, CommandError> { + let topics = store + .list_topics(&session_id_bg) + .map_err(|e| CommandError::new("LIST_TOPICS_ERROR", e.to_string()))?; + build_topic_summaries(store.as_ref(), topics) + }, + ) + .await + .map_err(|e| CommandError::new("LIST_TOPICS_ERROR", e.to_string()))? +} + /// 列出 Session 的 Topics 命令处理器 pub struct ListTopicsCommandHandler { store: Arc, @@ -139,20 +161,8 @@ async fn handle_list_topics( session_id: String, ctx: CommandContext, ) -> Result { - // 同步 rusqlite 查询移入 blocking 线程池,避免大库查询阻塞 tokio worker - // 饿死同连接上的其他 WS 命令(话题列表刷新是高频操作)。 - let store = handler.store.clone(); - let session_id_bg = session_id.clone(); - let summaries = tokio::task::spawn_blocking( - move || -> Result, CommandError> { - let topics = store - .list_topics(&session_id_bg) - .map_err(|e| CommandError::new("LIST_TOPICS_ERROR", e.to_string()))?; - build_topic_summaries(store.as_ref(), topics) - }, - ) - .await - .map_err(|e| CommandError::new("LIST_TOPICS_ERROR", e.to_string()))??; + let summaries = + list_topic_summaries_blocking(handler.store.clone(), &session_id).await?; let topics_json = serde_json::to_string(&summaries) .map_err(|e| CommandError::new("SERIALIZE_ERROR", e.to_string()))?; diff --git a/src/command/handlers/rename_topic.rs b/src/command/handlers/rename_topic.rs index 3a999a4..fe19a07 100644 --- a/src/command/handlers/rename_topic.rs +++ b/src/command/handlers/rename_topic.rs @@ -1,7 +1,6 @@ use crate::command::Command; use crate::command::context::CommandContext; use crate::command::handler::{CommandHandler, CommandMetadata}; -use crate::command::handlers::list_topics::build_topic_summaries; use crate::command::response::{CommandError, CommandResponse, MessageKind}; use crate::storage::SessionStore; use async_trait::async_trait; @@ -84,11 +83,12 @@ async fn handle_rename_topic( // 标题未变化时直接返回当前列表,避免无意义写入 if old_display == trimmed_title { - let topics = handler - .store - .list_topics(session_id) - .map_err(|e| CommandError::new("LIST_TOPICS_ERROR", e.to_string()))?; - let topic_summaries = build_topic_summaries(handler.store.as_ref(), topics)?; + let topic_summaries = + crate::command::handlers::list_topics::list_topic_summaries_blocking( + handler.store.clone(), + session_id, + ) + .await?; let topic_summaries_json = serde_json::to_string(&topic_summaries) .map_err(|e| CommandError::new("SERIALIZE_ERROR", e.to_string()))?; @@ -110,12 +110,12 @@ async fn handle_rename_topic( .map_err(|e| CommandError::new("RENAME_TOPIC_ERROR", e.to_string()))?; // 查询更新后的话题列表,返回给前端刷新侧边栏 - let topics = handler - .store - .list_topics(session_id) - .map_err(|e| CommandError::new("LIST_TOPICS_ERROR", e.to_string()))?; - - let topic_summaries = build_topic_summaries(handler.store.as_ref(), topics)?; + let topic_summaries = + crate::command::handlers::list_topics::list_topic_summaries_blocking( + handler.store.clone(), + session_id, + ) + .await?; let topic_summaries_json = serde_json::to_string(&topic_summaries) .map_err(|e| CommandError::new("SERIALIZE_ERROR", e.to_string()))?; diff --git a/src/command/handlers/session.rs b/src/command/handlers/session.rs index bdcc51b..5829e3e 100644 --- a/src/command/handlers/session.rs +++ b/src/command/handlers/session.rs @@ -1,7 +1,6 @@ use crate::command::Command; use crate::command::context::CommandContext; use crate::command::handler::{CommandHandler, CommandMetadata}; -use crate::command::handlers::list_topics::build_topic_summaries; use crate::command::response::{CommandError, CommandResponse, MessageKind}; use crate::gateway::session::SessionManager; use crate::storage::SessionStore; @@ -104,12 +103,12 @@ async fn handle_create_session( } // Query the full topic list so the frontend sidebar can update - let topics = handler - .store - .list_topics(session_id) - .map_err(|e| CommandError::new("LIST_TOPICS_ERROR", e.to_string()))?; - - let topic_summaries = build_topic_summaries(handler.store.as_ref(), topics)?; + let topic_summaries = + crate::command::handlers::list_topics::list_topic_summaries_blocking( + handler.store.clone(), + session_id, + ) + .await?; let topics_json = serde_json::to_string(&topic_summaries) .map_err(|e| CommandError::new("SERIALIZE_ERROR", e.to_string()))?;