From cae0b8e8061d8fa0fcf83cc0973472debc3efec5 Mon Sep 17 00:00:00 2001 From: link2xt Date: Wed, 15 Feb 2023 15:04:11 +0000 Subject: [PATCH] accounts: retry filesystem operations in migrate_account() --- CHANGELOG.md | 1 + src/accounts.rs | 61 ++++++++++++++++++++++++++++--------------------- 2 files changed, 36 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 21bafce20d..40a389172b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Changes - deltachat-rpc-client: use `dataclass` for `Account`, `Chat`, `Contact` and `Message` #4042 +- retry filesystem operations during account migration #4043 ### Fixes - deltachat-rpc-server: do not block stdin while processing the request. #4041 diff --git a/src/accounts.rs b/src/accounts.rs index 5ea6965e07..91c0e1a5fc 100644 --- a/src/accounts.rs +++ b/src/accounts.rs @@ -1,6 +1,7 @@ //! # Account manager module. use std::collections::BTreeMap; +use std::future::Future; use std::path::{Path, PathBuf}; use anyhow::{ensure, Context as _, Result}; @@ -150,27 +151,9 @@ impl Accounts { if let Some(cfg) = self.config.get_account(id) { let account_path = self.dir.join(cfg.dir); - // Spend up to 1 minute trying to remove the files. - // Files may remain locked up to 30 seconds due to r2d2 bug: - // https://github.com/sfackler/r2d2/issues/99 - let mut counter = 0; - loop { - counter += 1; - - if let Err(err) = fs::remove_dir_all(&account_path) - .await - .context("failed to remove account data") - { - if counter > 60 { - return Err(err); - } - - // Wait 1 second and try again. - tokio::time::sleep(std::time::Duration::from_millis(1000)).await; - } else { - break; - } - } + try_many_times(|| fs::remove_dir_all(&account_path)) + .await + .context("failed to remove account data")?; } self.config.remove_account(id).await?; @@ -202,10 +185,10 @@ impl Accounts { fs::create_dir_all(self.dir.join(&account_config.dir)) .await .context("failed to create dir")?; - fs::rename(&dbfile, &new_dbfile) + try_many_times(|| fs::rename(&dbfile, &new_dbfile)) .await .context("failed to rename dbfile")?; - fs::rename(&blobdir, &new_blobdir) + try_many_times(|| fs::rename(&blobdir, &new_blobdir)) .await .context("failed to rename blobdir")?; if walfile.exists() { @@ -229,11 +212,10 @@ impl Accounts { Ok(account_config.id) } Err(err) => { - // remove temp account - fs::remove_dir_all(std::path::PathBuf::from(&account_config.dir)) + let account_path = std::path::PathBuf::from(&account_config.dir); + try_many_times(|| fs::remove_dir_all(&account_path)) .await .context("failed to remove account data")?; - self.config.remove_account(account_config.id).await?; // set selection back @@ -488,6 +470,33 @@ impl Config { } } +/// Spend up to 1 minute trying to do the operation. +/// +/// Files may remain locked up to 30 seconds due to r2d2 bug: +/// +async fn try_many_times(f: F) -> std::result::Result<(), T> +where + F: Fn() -> Fut, + Fut: Future>, +{ + let mut counter = 0; + loop { + counter += 1; + + if let Err(err) = f().await { + if counter > 60 { + return Err(err); + } + + // Wait 1 second and try again. + tokio::time::sleep(std::time::Duration::from_millis(1000)).await; + } else { + break; + } + } + Ok(()) +} + /// Configuration of a single account. #[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] struct AccountConfig {