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.
This commit is contained in:
iequidoo
2026-07-16 20:03:49 -03:00
committed by iequidoo
parent 1a24bf612a
commit 0560c13ae5
2 changed files with 56 additions and 53 deletions
+46 -53
View File
@@ -115,10 +115,23 @@ impl Chatlist {
ChatId::new(0) ChatId::new(0)
}; };
// select with left join and minimum: macro_rules! last_visible_msg_id_in {
// ($chat_id:expr) => {
// - the inner select must use `hidden` and _not_ `m.hidden` concat!(
// which would refer the outer select and take a lot of time "
(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 // - `GROUP BY` is needed several messages may have the same
// timestamp // timestamp
// - the list starts with the newest chats // - the list starts with the newest chats
@@ -127,23 +140,18 @@ impl Chatlist {
// groups. Otherwise it would be hard to follow conversations. // groups. Otherwise it would be hard to follow conversations.
let ids = if let Some(query_contact_id) = query_contact_id { let ids = if let Some(query_contact_id) = query_contact_id {
// show chats shared with a given contact // show chats shared with a given contact
context.sql.query_map_vec( context.sql.query_map_vec(concat!(
"SELECT c.id, m.id "SELECT c.id, m.id
FROM chats c FROM chats c
LEFT JOIN msgs m LEFT JOIN msgs m
ON c.id=m.chat_id ON c.id=m.chat_id
AND m.id=( AND m.id=", last_visible_msg_id_in!("c.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 WHERE c.id>9
AND c.blocked!=1 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 GROUP BY c.id
ORDER BY c.archived=?3 DESC, IFNULL(NULLIF(m.timestamp,0),c.created_timestamp) DESC, m.id DESC;", ORDER BY c.archived=? DESC, IFNULL(NULLIF(m.timestamp,0),c.created_timestamp) DESC, m.id DESC"),
(MessageState::OutDraft, query_contact_id, ChatVisibility::Pinned), (query_contact_id, ChatVisibility::Pinned),
process_row, process_row,
).await? ).await?
} else if flag_archived_only { } else if flag_archived_only {
@@ -154,22 +162,22 @@ impl Chatlist {
context context
.sql .sql
.query_map_vec( .query_map_vec(
"SELECT c.id, m.id concat!(
"
SELECT c.id, m.id
FROM chats c FROM chats c
LEFT JOIN msgs m LEFT JOIN msgs m
ON c.id=m.chat_id ON c.id=m.chat_id
AND m.id=( AND m.id=",
SELECT id last_visible_msg_id_in!("c.id"),
FROM msgs "
WHERE chat_id=c.id
AND (hidden=0 OR state=?)
ORDER BY timestamp DESC, id DESC LIMIT 1)
WHERE c.id>9 WHERE c.id>9
AND c.blocked!=1 AND c.blocked!=1
AND c.archived=1 AND c.archived=1
GROUP BY c.id GROUP BY c.id
ORDER BY IFNULL(NULLIF(m.timestamp,0),c.created_timestamp) DESC, m.id DESC;", ORDER BY IFNULL(NULLIF(m.timestamp,0),c.created_timestamp) DESC, m.id DESC"
(MessageState::OutDraft,), ),
(),
process_row, process_row,
) )
.await? .await?
@@ -188,24 +196,19 @@ impl Chatlist {
let str_like_cmd = format!("%{}%", query.to_lowercase()); let str_like_cmd = format!("%{}%", query.to_lowercase());
context context
.sql .sql
.query_map_vec( .query_map_vec(concat!(
"SELECT c.id, m.id "SELECT c.id, m.id
FROM chats c FROM chats c
LEFT JOIN msgs m LEFT JOIN msgs m
ON c.id=m.chat_id ON c.id=m.chat_id
AND m.id=( AND m.id=", last_visible_msg_id_in!("c.id"), "
SELECT id WHERE c.id>9 AND c.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 c.blocked!=1 AND c.blocked!=1
AND IFNULL(c.name_normalized,c.name) LIKE ?3 AND IFNULL(c.name_normalized,c.name) LIKE ?
AND (NOT ?4 OR EXISTS (SELECT 1 FROM msgs m WHERE m.chat_id = c.id AND m.state == ?5 AND hidden=0)) 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 GROUP BY c.id
ORDER BY IFNULL(NULLIF(m.timestamp,0),c.created_timestamp) DESC, m.id DESC;", 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), (skip_id, str_like_cmd, only_unread, MessageState::InFresh),
process_row, process_row,
) )
.await? .await?
@@ -237,25 +240,20 @@ impl Chatlist {
}) })
.collect::<std::result::Result<Vec<_>, _>>() .collect::<std::result::Result<Vec<_>, _>>()
}; };
context.sql.query_map( context.sql.query_map(concat!(
"SELECT c.id, c.type, c.param, m.id "SELECT c.id, c.type, c.param, m.id
FROM chats c FROM chats c
LEFT JOIN msgs m LEFT JOIN msgs m
ON c.id=m.chat_id ON c.id=m.chat_id
AND m.id=( AND m.id=", last_visible_msg_id_in!("c.id"), "
SELECT id
FROM msgs
WHERE chat_id=c.id
AND (hidden=0 OR state=?)
ORDER BY timestamp DESC, id DESC LIMIT 1)
WHERE c.id>9 AND c.id!=? WHERE c.id>9 AND c.id!=?
AND c.blocked=0 AND c.blocked=0
AND NOT c.archived=? 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)) 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 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, Chattype::Group, ContactId::SELF,
sort_id_up, ChatVisibility::Pinned, sort_id_up, ChatVisibility::Pinned,
), ),
@@ -264,23 +262,18 @@ impl Chatlist {
).await? ).await?
} else { } else {
// show normal chatlist // show normal chatlist
context.sql.query_map_vec( context.sql.query_map_vec(concat!(
"SELECT c.id, m.id "SELECT c.id, m.id
FROM chats c FROM chats c
LEFT JOIN msgs m LEFT JOIN msgs m
ON c.id=m.chat_id ON c.id=m.chat_id
AND m.id=( AND m.id=", last_visible_msg_id_in!("c.id"), "
SELECT id
FROM msgs
WHERE chat_id=c.id
AND (hidden=0 OR state=?)
ORDER BY timestamp DESC, id DESC LIMIT 1)
WHERE c.id>9 AND c.id!=? WHERE c.id>9 AND c.id!=?
AND (c.blocked=0 OR c.blocked=2) AND (c.blocked=0 OR c.blocked=2)
AND NOT c.archived=? AND NOT c.archived=?
GROUP BY c.id 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;", 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), (skip_id, ChatVisibility::Archived, ChatVisibility::Pinned),
process_row, process_row,
).await? ).await?
}; };
+10
View File
@@ -2477,6 +2477,16 @@ UPDATE msgs SET state=24 WHERE state=18; -- Change OutPreparing to OutFailed.
.await?; .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 let new_version = sql
.get_raw_config_int(VERSION_CFG) .get_raw_config_int(VERSION_CFG)
.await? .await?