From 07247ed140c421a21ac72d95c8fa8a91b5e1e4a8 Mon Sep 17 00:00:00 2001 From: oudecheng <13802883547@139.com> Date: Tue, 4 Aug 2026 15:18:21 +0800 Subject: [PATCH] =?UTF-8?q?perf(storage):=20get=5Ftopic=5Fmessage=5Fcount?= =?UTF-8?q?=20=E6=94=B9=E7=94=A8=20COUNT(*)=20=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 原实现调用 load_messages_for_topic 加载该 topic 的所有消息 (含 content、tool_calls_json 等大字段反序列化)到内存, 仅为调用 .len() 计数。长会话可能加载数千条消息,且在 list_sessions 列出所有 topic 时会按 topic 数量倍增。 改为 SELECT COUNT(*) FROM messages WHERE topic_id = ?1, 在数据库侧计数:无需反序列化任何字段,命中已有的 idx_messages_topic_seq(topic_id, seq) 索引。 对 N 条消息的 topic,从 O(N) 内存 + 反序列化降为 O(1) 内存 + 索引扫描。新增测试覆盖计数正确性与隔离性。 --- src/storage/mod.rs | 15 ++++++++++++--- src/storage/tests.rs | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/src/storage/mod.rs b/src/storage/mod.rs index 493675c..ad9ae7b 100644 --- a/src/storage/mod.rs +++ b/src/storage/mod.rs @@ -1581,10 +1581,19 @@ impl SessionStore { } } - /// 获取指定话题的消息数量(动态计算,确保准确) + /// 获取指定话题的消息数量。 + /// + /// 使用 `SELECT COUNT(*)` 在数据库侧计数,避免将所有消息 + /// (含 content、tool_calls_json 等大字段反序列化)加载到内存。 + /// 查询命中 `idx_messages_topic_seq(topic_id, seq)` 索引。 pub fn get_topic_message_count(&self, topic_id: &str) -> Result { - self.load_messages_for_topic(topic_id, None) - .map(|msgs| msgs.len()) + let conn = self.pool.get()?; + let count: i64 = conn.query_row( + "SELECT COUNT(*) FROM messages WHERE topic_id = ?1", + params![topic_id], + |row| row.get(0), + )?; + Ok(count as usize) } pub fn load_all_messages(&self, session_id: &str) -> Result, StorageError> { diff --git a/src/storage/tests.rs b/src/storage/tests.rs index 1cefb3f..ef4553e 100644 --- a/src/storage/tests.rs +++ b/src/storage/tests.rs @@ -687,3 +687,41 @@ fn test_scheduler_job_roundtrip_and_runtime_update() { assert_eq!(fetched.run_count, 1); assert_eq!(fetched.completed_at, Some(1_700_000_000_100)); } + +#[test] +fn test_get_topic_message_count_uses_count_query() { + let store = SessionStore::in_memory().unwrap(); + let session = store.create_cli_session(Some("topic-count")).unwrap(); + let topic = store + .create_topic(&session.id, "topic-1", None) + .unwrap(); + + // 初始计数为 0 + assert_eq!(store.get_topic_message_count(&topic.id).unwrap(), 0); + + // 追加 3 条带 topic_id 的消息 + for content in ["m1", "m2", "m3"] { + store + .append_message_with_topic( + &session.id, + Some(&topic.id), + &ChatMessage::user(content), + ) + .unwrap(); + } + + // 计数应为 3,且不需要加载消息内容 + assert_eq!(store.get_topic_message_count(&topic.id).unwrap(), 3); + + // 另一个 topic 的计数应为 0(隔离验证) + let other_topic = store + .create_topic(&session.id, "topic-2", None) + .unwrap(); + assert_eq!(store.get_topic_message_count(&other_topic.id).unwrap(), 0); + + // 不存在的 topic_id 返回 0 + assert_eq!( + store.get_topic_message_count("topic:nonexistent").unwrap(), + 0 + ); +}