From 0560c13ae5cbef33b97e18c65c7f162df613b4de Mon Sep 17 00:00:00 2001 From: iequidoo Date: Fri, 13 Mar 2026 10:41:19 -0300 Subject: [PATCH] perf: Add timestamp to msgs_index7 and speed up Chatlist::try_load() (#7848) This speeds up chatlisting by 3--4 times on my biggest profile, so the difference is visually noticeable, particularly after dropping disk caches. Before, `msgs_index2` was used which has less ordering and requires a full scan of `msgs` table. --- src/chatlist.rs | 99 ++++++++++++++++++++----------------------- src/sql/migrations.rs | 10 +++++ 2 files changed, 56 insertions(+), 53 deletions(-) diff --git a/src/chatlist.rs b/src/chatlist.rs index 0563ea70e..d954b3043 100644 --- a/src/chatlist.rs +++ b/src/chatlist.rs @@ -115,10 +115,23 @@ impl Chatlist { ChatId::new(0) }; - // select with left join and minimum: - // - // - the inner select must use `hidden` and _not_ `m.hidden` - // which would refer the outer select and take a lot of time + macro_rules! last_visible_msg_id_in { + ($chat_id:expr) => { + concat!( + " +(SELECT id FROM msgs WHERE + -- state=`OutDraft`. + state=19 AND hidden=1 AND chat_id=", + $chat_id, + " OR + -- `InFresh`...`OutDelivered` inclusive, except `OutDraft`. + state IN (10,13,16,20,24,26) AND hidden=0 AND chat_id=", + $chat_id, + " +ORDER BY timestamp DESC, id DESC LIMIT 1)" + ) + }; + } // - `GROUP BY` is needed several messages may have the same // timestamp // - the list starts with the newest chats @@ -127,23 +140,18 @@ impl Chatlist { // groups. Otherwise it would be hard to follow conversations. let ids = if let Some(query_contact_id) = query_contact_id { // show chats shared with a given contact - context.sql.query_map_vec( + context.sql.query_map_vec(concat!( "SELECT c.id, m.id FROM chats c LEFT JOIN msgs m ON c.id=m.chat_id - AND m.id=( - SELECT id - FROM msgs - WHERE chat_id=c.id - AND (hidden=0 OR state=?1) - ORDER BY timestamp DESC, id DESC LIMIT 1) + AND m.id=", last_visible_msg_id_in!("c.id"), " WHERE c.id>9 AND c.blocked!=1 - AND c.id IN(SELECT chat_id FROM chats_contacts WHERE contact_id=?2 AND add_timestamp >= remove_timestamp) + AND c.id IN(SELECT chat_id FROM chats_contacts WHERE contact_id=? AND add_timestamp >= remove_timestamp) GROUP BY c.id - ORDER BY c.archived=?3 DESC, IFNULL(NULLIF(m.timestamp,0),c.created_timestamp) DESC, m.id DESC;", - (MessageState::OutDraft, query_contact_id, ChatVisibility::Pinned), + ORDER BY c.archived=? DESC, IFNULL(NULLIF(m.timestamp,0),c.created_timestamp) DESC, m.id DESC"), + (query_contact_id, ChatVisibility::Pinned), process_row, ).await? } else if flag_archived_only { @@ -154,22 +162,22 @@ impl Chatlist { context .sql .query_map_vec( - "SELECT c.id, m.id + concat!( + " + SELECT c.id, m.id FROM chats c LEFT JOIN msgs m ON c.id=m.chat_id - AND m.id=( - SELECT id - FROM msgs - WHERE chat_id=c.id - AND (hidden=0 OR state=?) - ORDER BY timestamp DESC, id DESC LIMIT 1) + AND m.id=", + last_visible_msg_id_in!("c.id"), + " WHERE c.id>9 AND c.blocked!=1 AND c.archived=1 GROUP BY c.id - ORDER BY IFNULL(NULLIF(m.timestamp,0),c.created_timestamp) DESC, m.id DESC;", - (MessageState::OutDraft,), + ORDER BY IFNULL(NULLIF(m.timestamp,0),c.created_timestamp) DESC, m.id DESC" + ), + (), process_row, ) .await? @@ -188,24 +196,19 @@ impl Chatlist { let str_like_cmd = format!("%{}%", query.to_lowercase()); context .sql - .query_map_vec( + .query_map_vec(concat!( "SELECT c.id, m.id FROM chats c LEFT JOIN msgs m ON c.id=m.chat_id - AND m.id=( - SELECT id - FROM msgs - WHERE chat_id=c.id - AND (hidden=0 OR state=?1) - ORDER BY timestamp DESC, id DESC LIMIT 1) - WHERE c.id>9 AND c.id!=?2 + AND m.id=", last_visible_msg_id_in!("c.id"), " + WHERE c.id>9 AND c.id!=? AND c.blocked!=1 - AND IFNULL(c.name_normalized,c.name) LIKE ?3 - AND (NOT ?4 OR EXISTS (SELECT 1 FROM msgs m WHERE m.chat_id = c.id AND m.state == ?5 AND hidden=0)) + AND IFNULL(c.name_normalized,c.name) LIKE ? + AND (NOT ? OR EXISTS (SELECT 1 FROM msgs m WHERE m.chat_id = c.id AND m.state == ? AND hidden=0)) GROUP BY c.id - ORDER BY IFNULL(NULLIF(m.timestamp,0),c.created_timestamp) DESC, m.id DESC;", - (MessageState::OutDraft, skip_id, str_like_cmd, only_unread, MessageState::InFresh), + ORDER BY IFNULL(NULLIF(m.timestamp,0),c.created_timestamp) DESC, m.id DESC"), + (skip_id, str_like_cmd, only_unread, MessageState::InFresh), process_row, ) .await? @@ -237,25 +240,20 @@ impl Chatlist { }) .collect::, _>>() }; - context.sql.query_map( + context.sql.query_map(concat!( "SELECT c.id, c.type, c.param, m.id FROM chats c LEFT JOIN msgs m ON c.id=m.chat_id - AND m.id=( - SELECT id - FROM msgs - WHERE chat_id=c.id - AND (hidden=0 OR state=?) - ORDER BY timestamp DESC, id DESC LIMIT 1) + AND m.id=", last_visible_msg_id_in!("c.id"), " WHERE c.id>9 AND c.id!=? AND c.blocked=0 AND NOT c.archived=? AND (c.type!=? OR c.id IN(SELECT chat_id FROM chats_contacts WHERE contact_id=? AND add_timestamp >= remove_timestamp)) GROUP BY c.id - ORDER BY c.id=? DESC, c.archived=? DESC, IFNULL(NULLIF(m.timestamp,0),c.created_timestamp) DESC, m.id DESC;", + ORDER BY c.id=? DESC, c.archived=? DESC, IFNULL(NULLIF(m.timestamp,0),c.created_timestamp) DESC, m.id DESC"), ( - MessageState::OutDraft, skip_id, ChatVisibility::Archived, + skip_id, ChatVisibility::Archived, Chattype::Group, ContactId::SELF, sort_id_up, ChatVisibility::Pinned, ), @@ -264,23 +262,18 @@ impl Chatlist { ).await? } else { // show normal chatlist - context.sql.query_map_vec( + context.sql.query_map_vec(concat!( "SELECT c.id, m.id FROM chats c LEFT JOIN msgs m ON c.id=m.chat_id - AND m.id=( - SELECT id - FROM msgs - WHERE chat_id=c.id - AND (hidden=0 OR state=?) - ORDER BY timestamp DESC, id DESC LIMIT 1) + AND m.id=", last_visible_msg_id_in!("c.id"), " WHERE c.id>9 AND c.id!=? AND (c.blocked=0 OR c.blocked=2) AND NOT c.archived=? GROUP BY c.id - ORDER BY c.id=0 DESC, c.archived=? DESC, IFNULL(NULLIF(m.timestamp,0),c.created_timestamp) DESC, m.id DESC;", - (MessageState::OutDraft, skip_id, ChatVisibility::Archived, ChatVisibility::Pinned), + ORDER BY c.id=0 DESC, c.archived=? DESC, IFNULL(NULLIF(m.timestamp,0),c.created_timestamp) DESC, m.id DESC"), + (skip_id, ChatVisibility::Archived, ChatVisibility::Pinned), process_row, ).await? }; diff --git a/src/sql/migrations.rs b/src/sql/migrations.rs index a0fd79eda..1c55a2f59 100644 --- a/src/sql/migrations.rs +++ b/src/sql/migrations.rs @@ -2477,6 +2477,16 @@ UPDATE msgs SET state=24 WHERE state=18; -- Change OutPreparing to OutFailed. .await?; } + inc_and_check(&mut migration_version, 156)?; + if dbversion < migration_version { + sql.execute_migration( + "DROP INDEX IF EXISTS msgs_index7; + CREATE INDEX msgs_index7 ON msgs (state, hidden, chat_id, timestamp);", + migration_version, + ) + .await?; + } + let new_version = sql .get_raw_config_int(VERSION_CFG) .await?