From 83464a882e99eb641954a6bbd91daf63c0b07b52 Mon Sep 17 00:00:00 2001 From: link2xt Date: Sat, 19 Mar 2022 22:39:13 +0000 Subject: [PATCH] Optimize markseen_msgs Use a single SELECT statement for all messages and start ephemeral timers for all messages at once. --- CHANGELOG.md | 1 + src/ephemeral.rs | 32 ++++++++++++++++++++++ src/message.rs | 70 ++++++++++++++++++++---------------------------- 3 files changed, 62 insertions(+), 41 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c2c43b2d9..e2b168fbd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ - place common headers like `From:` before the large `Autocrypt:` header #3079 - keep track of securejoin joiner status in database to survive restarts #2920 - remove never used `SentboxMove` option #3111 +- optimize `markseen_msgs` #3141 ### Fixes - Fix a bug where sometimes the file extension of a long filename containing a dot was cropped #3098 diff --git a/src/ephemeral.rs b/src/ephemeral.rs index 668407e76..c2b381468 100644 --- a/src/ephemeral.rs +++ b/src/ephemeral.rs @@ -76,6 +76,7 @@ use crate::download::MIN_DELETE_SERVER_AFTER; use crate::events::EventType; use crate::message::{Message, MessageState, MsgId, Viewtype}; use crate::mimeparser::SystemMessage; +use crate::sql; use crate::stock_str; use std::cmp::max; @@ -298,6 +299,37 @@ impl MsgId { } } +pub(crate) async fn start_ephemeral_timers_msgids( + context: &Context, + msg_ids: &[MsgId], +) -> Result<()> { + let msg_ids: Vec<&dyn crate::ToSql> = msg_ids + .iter() + .map(|msg_id| msg_id as &dyn crate::ToSql) + .collect(); + let now = time(); + let count = context + .sql + .execute( + format!( + "UPDATE msgs SET ephemeral_timestamp = ? + ephemeral_timer + WHERE (ephemeral_timestamp == 0 OR ephemeral_timestamp > ? + ephemeral_timer) AND ephemeral_timer > 0 + AND id IN ({})", + sql::repeat_vars(msg_ids.len())? + ), + rusqlite::params_from_iter( + std::iter::once(&now as &dyn crate::ToSql) + .chain(std::iter::once(&now as &dyn crate::ToSql)) + .chain(msg_ids), + ), + ) + .await?; + if count > 0 { + schedule_ephemeral_task(context).await; + } + Ok(()) +} + /// Deletes messages which are expired according to /// `delete_device_after` setting or `ephemeral_timestamp` column. /// diff --git a/src/message.rs b/src/message.rs index d3e200dcf..4ff3460ac 100644 --- a/src/message.rs +++ b/src/message.rs @@ -20,7 +20,7 @@ use crate::dc_tools::{ dc_read_file, dc_timestamp_to_str, dc_truncate, time, }; use crate::download::DownloadState; -use crate::ephemeral::Timer as EphemeralTimer; +use crate::ephemeral::{start_ephemeral_timers_msgids, Timer as EphemeralTimer}; use crate::events::EventType; use crate::job::{self, Action}; use crate::log::LogExt; @@ -28,6 +28,7 @@ use crate::mimeparser::{parse_message_id, FailureReport, SystemMessage}; use crate::param::{Param, Params}; use crate::pgp::split_armored_data; use crate::scheduler::InterruptInfo; +use crate::sql; use crate::stock_str; use crate::summary::Summary; @@ -1285,50 +1286,37 @@ pub async fn markseen_msgs(context: &Context, msg_ids: Vec) -> Result<()> return Ok(()); } - let conn = context.sql.get_conn().await?; - let msgs = async_std::task::spawn_blocking(move || -> Result<_> { - let mut stmt = conn.prepare_cached(concat!( - "SELECT", - " m.chat_id AS chat_id,", - " m.state AS state,", - " c.blocked AS blocked", - " FROM msgs m LEFT JOIN chats c ON c.id=m.chat_id", - " WHERE m.id=? AND m.chat_id>9" - ))?; + let msgs = context + .sql + .query_map( + format!( + "SELECT + m.id AS id, + m.chat_id AS chat_id, + m.state AS state, + c.blocked AS blocked + FROM msgs m LEFT JOIN chats c ON c.id=m.chat_id + WHERE m.id IN ({}) AND m.chat_id>9", + sql::repeat_vars(msg_ids.len())? + ), + rusqlite::params_from_iter(&msg_ids), + |row| { + let id: MsgId = row.get("id")?; + let chat_id: ChatId = row.get("chat_id")?; + let state: MessageState = row.get("state")?; + let blocked: Option = row.get("blocked")?; + Ok((id, chat_id, state, blocked.unwrap_or_default())) + }, + |rows| rows.collect::, _>>().map_err(Into::into), + ) + .await?; - let mut msgs = Vec::with_capacity(msg_ids.len()); - for id in msg_ids.into_iter() { - let query_res = stmt.query_row(paramsv![id], |row| { - Ok(( - row.get::<_, ChatId>("chat_id")?, - row.get::<_, MessageState>("state")?, - row.get::<_, Option>("blocked")? - .unwrap_or_default(), - )) - }); - if let Err(rusqlite::Error::QueryReturnedNoRows) = query_res { - continue; - } - let (chat_id, state, blocked) = query_res.map_err(Into::::into)?; - msgs.push((id, chat_id, state, blocked)); - } - drop(stmt); - drop(conn); - Ok(msgs) - }) - .await?; + start_ephemeral_timers_msgids(context, &msg_ids) + .await + .context("failed to start ephemeral timers")?; let mut updated_chat_ids = BTreeSet::new(); - for (id, curr_chat_id, curr_state, curr_blocked) in msgs.into_iter() { - if let Err(err) = id.start_ephemeral_timer(context).await { - error!( - context, - "Failed to start ephemeral timer for message {}: {}", id, err - ); - continue; - } - if curr_blocked == Blocked::Not && (curr_state == MessageState::InFresh || curr_state == MessageState::InNoticed) {