mirror of
https://github.com/chatmail/core.git
synced 2026-04-28 10:56:29 +03:00
Fix imex race condition, (#2255)
fix #2254: if the DB was closed without calling stop_io() and then an interrupt arrives (e.g. incoming message), the db was corrupted. * Add result.log() for logging with less boilerplate code * Bugfix: Resultify housekeeping() to make it abort if the db is closed instead of just deleting everything * Require the UI to call dc_stop_io() before backup export * Prepare a bit better for closed-db: Resultify get_uidvalidity and get_uid_next and let job::load_next() wait until the db is open About the bug (before this PR): if the DB was closed without calling stop_io() and then an interrupt arrives (e.g. incoming message): - I don't know if it downloads the message, but of course at some point the process of receiving the message will fail - In my test, DC is just in the process of moving a message when the imex starts, but then can't delete the job or update the msg server_uid - Then, when job::load_next() is called, no job can be loaded. That's why it calls `load_housekeeping_job()`. As `load_housekeeping_job()` can't load the time of the last housekeeping, it assumes we never ran housekeeping and returns a new Housekeeping job, which is immediately executed. - housekeeping can't find any blobs referenced in the db and therefore deletes almost all blobs.
This commit is contained in:
@@ -564,8 +564,8 @@ impl Imap {
|
||||
.uid_validity
|
||||
.with_context(|| format!("No UIDVALIDITY for folder {}", folder))?;
|
||||
|
||||
let old_uid_validity = get_uidvalidity(context, folder).await;
|
||||
let old_uid_next = get_uid_next(context, folder).await;
|
||||
let old_uid_validity = get_uidvalidity(context, folder).await?;
|
||||
let old_uid_next = get_uid_next(context, folder).await?;
|
||||
|
||||
if new_uid_validity == old_uid_validity {
|
||||
let new_emails = if newly_selected == NewlySelected::No {
|
||||
@@ -658,7 +658,7 @@ impl Imap {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
let old_uid_next = get_uid_next(context, folder.as_ref()).await;
|
||||
let old_uid_next = get_uid_next(context, folder.as_ref()).await?;
|
||||
|
||||
let msgs = if fetch_existing_msgs {
|
||||
self.prefetch_existing_msgs().await?
|
||||
@@ -1733,16 +1733,15 @@ pub(crate) async fn set_uid_next(context: &Context, folder: &str, uid_next: u32)
|
||||
/// This method returns the uid_next from the last time we fetched messages.
|
||||
/// We can compare this to the current uid_next to find out whether there are new messages
|
||||
/// and fetch from this value on to get all new messages.
|
||||
async fn get_uid_next(context: &Context, folder: &str) -> u32 {
|
||||
context
|
||||
async fn get_uid_next(context: &Context, folder: &str) -> Result<u32> {
|
||||
Ok(context
|
||||
.sql
|
||||
.query_get_value(
|
||||
context,
|
||||
.query_get_value_result(
|
||||
"SELECT uid_next FROM imap_sync WHERE folder=?;",
|
||||
paramsv![folder],
|
||||
)
|
||||
.await
|
||||
.unwrap_or(0)
|
||||
.await?
|
||||
.unwrap_or(0))
|
||||
}
|
||||
|
||||
pub(crate) async fn set_uidvalidity(
|
||||
@@ -1761,16 +1760,15 @@ pub(crate) async fn set_uidvalidity(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn get_uidvalidity(context: &Context, folder: &str) -> u32 {
|
||||
context
|
||||
async fn get_uidvalidity(context: &Context, folder: &str) -> Result<u32> {
|
||||
Ok(context
|
||||
.sql
|
||||
.query_get_value(
|
||||
context,
|
||||
.query_get_value_result(
|
||||
"SELECT uidvalidity FROM imap_sync WHERE folder=?;",
|
||||
paramsv![folder],
|
||||
)
|
||||
.await
|
||||
.unwrap_or(0)
|
||||
.await?
|
||||
.unwrap_or(0))
|
||||
}
|
||||
|
||||
/// Deprecated, use get_uid_next() and get_uidvalidity()
|
||||
@@ -1879,17 +1877,17 @@ mod tests {
|
||||
#[async_std::test]
|
||||
async fn test_set_uid_next_validity() {
|
||||
let t = TestContext::new_alice().await;
|
||||
assert_eq!(get_uid_next(&t.ctx, "Inbox").await, 0);
|
||||
assert_eq!(get_uidvalidity(&t.ctx, "Inbox").await, 0);
|
||||
assert_eq!(get_uid_next(&t.ctx, "Inbox").await.unwrap(), 0);
|
||||
assert_eq!(get_uidvalidity(&t.ctx, "Inbox").await.unwrap(), 0);
|
||||
|
||||
set_uidvalidity(&t.ctx, "Inbox", 7).await.unwrap();
|
||||
assert_eq!(get_uidvalidity(&t.ctx, "Inbox").await, 7);
|
||||
assert_eq!(get_uid_next(&t.ctx, "Inbox").await, 0);
|
||||
assert_eq!(get_uidvalidity(&t.ctx, "Inbox").await.unwrap(), 7);
|
||||
assert_eq!(get_uid_next(&t.ctx, "Inbox").await.unwrap(), 0);
|
||||
|
||||
set_uid_next(&t.ctx, "Inbox", 5).await.unwrap();
|
||||
set_uidvalidity(&t.ctx, "Inbox", 6).await.unwrap();
|
||||
assert_eq!(get_uid_next(&t.ctx, "Inbox").await, 5);
|
||||
assert_eq!(get_uidvalidity(&t.ctx, "Inbox").await, 6);
|
||||
assert_eq!(get_uid_next(&t.ctx, "Inbox").await.unwrap(), 5);
|
||||
assert_eq!(get_uidvalidity(&t.ctx, "Inbox").await.unwrap(), 6);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user