mirror of
https://github.com/chatmail/core.git
synced 2026-04-22 07: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:
@@ -2,17 +2,17 @@
|
||||
//!
|
||||
//! This private module is only compiled for test runs.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt;
|
||||
use std::ops::Deref;
|
||||
use std::str::FromStr;
|
||||
use std::time::{Duration, Instant};
|
||||
use std::{collections::BTreeMap, panic};
|
||||
|
||||
use ansi_term::Color;
|
||||
use async_std::future::Future;
|
||||
use async_std::path::PathBuf;
|
||||
use async_std::pin::Pin;
|
||||
use async_std::sync::{Arc, RwLock};
|
||||
use async_std::{channel, pin::Pin};
|
||||
use chat::ChatItem;
|
||||
use once_cell::sync::Lazy;
|
||||
use tempfile::{tempdir, TempDir};
|
||||
@@ -50,6 +50,8 @@ pub(crate) struct TestContext {
|
||||
recv_idx: RwLock<u32>,
|
||||
/// Functions to call for events received.
|
||||
event_sinks: Arc<RwLock<Vec<Box<EventSink>>>>,
|
||||
/// Receives panics from sinks ("sink" means "event handler" here)
|
||||
poison_receiver: channel::Receiver<String>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for TestContext {
|
||||
@@ -97,7 +99,15 @@ impl TestContext {
|
||||
let events = ctx.get_event_emitter();
|
||||
let event_sinks: Arc<RwLock<Vec<Box<EventSink>>>> = Arc::new(RwLock::new(Vec::new()));
|
||||
let sinks = Arc::clone(&event_sinks);
|
||||
let (poison_sender, poison_receiver) = channel::bounded(1);
|
||||
async_std::task::spawn(async move {
|
||||
// Make sure that the test fails if there is a panic on this thread here:
|
||||
let orig_hook = panic::take_hook();
|
||||
panic::set_hook(Box::new(move |panic_info| {
|
||||
poison_sender.try_send(panic_info.to_string()).ok();
|
||||
orig_hook(panic_info);
|
||||
}));
|
||||
|
||||
while let Some(event) = events.recv().await {
|
||||
{
|
||||
let sinks = sinks.read().await;
|
||||
@@ -114,6 +124,7 @@ impl TestContext {
|
||||
dir,
|
||||
recv_idx: RwLock::new(0),
|
||||
event_sinks,
|
||||
poison_receiver,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -410,6 +421,14 @@ impl Deref for TestContext {
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TestContext {
|
||||
fn drop(&mut self) {
|
||||
if let Ok(p) = self.poison_receiver.try_recv() {
|
||||
panic!(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A raw message as it was scheduled to be sent.
|
||||
///
|
||||
/// This is a raw message, probably in the shape DC was planning to send it but not having
|
||||
|
||||
Reference in New Issue
Block a user