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()))?;