From 5e29cae81acd7a7264499c52d1695b571ae10ee0 Mon Sep 17 00:00:00 2001 From: Hocuri Date: Tue, 5 Oct 2021 10:57:34 +0200 Subject: [PATCH] Fix: Don't update quota in an endless loop (#2726) The problem was: When opening the connectivity view while there is no network, get_connectivity_html() calls schedule_quota_update(), which schedules a UpdateRecentQuota job. But in update_recent_quota(), connecting fails and a ConnectivityChanged event is emitted (connectivity changes from Error to Connecting and back). Therefore the UI calls get_connectivity_html() again, and the loop is complete. This made the UI completely unresponsible. To reproduce, just turn wi-fi off and open the connectivity view. The fix is: schedule_quota_update() now only schedules a new job if there is no old job. To prevent the possible (though probably unlikely) problem that an old quota update job has a backoff of, like, a day and therefore quota is not updated, I reduced the backoff time for quota jobs to 10s. Fixes possibly https://github.com/deltachat/deltachat-android/issues/2043, but we should "re-try" this --- src/job.rs | 27 ++++++++++++++++++--------- src/quota.rs | 13 +++++++------ 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/src/job.rs b/src/job.rs index 55ab03e54..778729ceb 100644 --- a/src/job.rs +++ b/src/job.rs @@ -1084,7 +1084,7 @@ pub(crate) async fn perform_job(context: &Context, mut connection: Connection<'_ "{} thread increases job {} tries to {}", &connection, job, tries ); job.tries = tries; - let time_offset = get_backoff_time_offset(tries); + let time_offset = get_backoff_time_offset(tries, job.action); job.desired_timestamp = time() + time_offset; info!( context, @@ -1170,15 +1170,24 @@ async fn perform_job_action( try_res } -fn get_backoff_time_offset(tries: u32) -> i64 { - let n = 2_i32.pow(tries - 1) * 60; - let mut rng = thread_rng(); - let r: i32 = rng.gen(); - let mut seconds = r % (n + 1); - if seconds < 1 { - seconds = 1; +fn get_backoff_time_offset(tries: u32, action: Action) -> i64 { + match action { + // Just try every 10s to update the quota + // If all retries are exhausted, a new job will be created when the quota information is needed + Action::UpdateRecentQuota => 10, + + _ => { + // Exponential backoff + let n = 2_i32.pow(tries - 1) * 60; + let mut rng = thread_rng(); + let r: i32 = rng.gen(); + let mut seconds = r % (n + 1); + if seconds < 1 { + seconds = 1; + } + seconds as i64 + } } - seconds as i64 } async fn send_mdn(context: &Context, msg: &Message) -> Result<()> { diff --git a/src/quota.rs b/src/quota.rs index 3b0b55905..896c2348b 100644 --- a/src/quota.rs +++ b/src/quota.rs @@ -110,12 +110,13 @@ pub fn needs_quota_warning(curr_percentage: u64, warned_at_percentage: u64) -> b impl Context { // Adds a job to update `quota.recent` pub(crate) async fn schedule_quota_update(&self) -> Result<()> { - job::kill_action(self, Action::UpdateRecentQuota).await?; - job::add( - self, - job::Job::new(Action::UpdateRecentQuota, 0, Params::new(), 0), - ) - .await?; + if !job::action_exists(self, Action::UpdateRecentQuota).await? { + job::add( + self, + job::Job::new(Action::UpdateRecentQuota, 0, Params::new(), 0), + ) + .await?; + } Ok(()) }