From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from gate001.proxmox.com (gate001.proxmox.com [IPv6:2a0f:8001:1:32::40]) by lore.proxmox.com (Postfix) with ESMTPS id 65BA41FF0BC for ; Thu, 15 Oct 2026 14:17:06 +0200 (CEST) Received: from gate001.proxmox.com (localhost.localdomain [127.0.0.1]) by gate001.proxmox.com (Proxmox) with ESMTP id 96824213E1; Thu, 15 Oct 2026 14:17:03 +0200 (CEST) From: Jakob Klocker To: pbs-devel@lists.proxmox.com Subject: [PATCH proxmox-backup v4 2/5] server: pull: run blocking file operations on the blocking pool Date: Thu, 15 Oct 2026 14:16:56 +0200 Message-ID: <20261015121659.239652-3-j.klocker@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20261015121659.239652-1-j.klocker@proxmox.com> References: <20261015121659.239652-1-j.klocker@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 1 AWL -0.672 Adjusted score from AWL reputation of From: address DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment (newer systems) KAM_LAZY_DOMAIN_SECURITY 1 Sending domain does not have any anti-forgery methods RDNS_NONE 1.274 Delivered to internal network by a host with no rDNS SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_NONE 0.001 SPF: sender does not publish an SPF Record Message-ID-Hash: TSSGRUOW4D6RB52NI34YUPQU2H32F3MY X-Message-ID-Hash: TSSGRUOW4D6RB52NI34YUPQU2H32F3MY X-MailFrom: jklocker@dev.proxmox.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox Backup Server development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: The atomic renames and the unreferenced-file cleanup are blocking operations that ran directly on the runtime's worker threads, stalling other tasks scheduled there for their duration. Use the tokio counterpart for the renames, the removes, and move the cleanup onto the blocking thread pool. Note that tokio::fs runs these on the blocking thread pool, which carries some overhead over std::fs, but that is acceptable here as these are one-off operations rather than tight loops. Reviewed-by: Christian Ebner Tested-by: Christian Ebner Signed-off-by: Jakob Klocker --- src/server/pull.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/server/pull.rs b/src/server/pull.rs index 501c05a1b..1e24b038b 100644 --- a/src/server/pull.rs +++ b/src/server/pull.rs @@ -455,7 +455,7 @@ async fn pull_single_archive<'a>( .with_context(|| archive_prefix.clone())? .is_none() { - let _ = std::fs::remove_file(&tmp_path); + let _ = tokio::fs::remove_file(&tmp_path).await; bail!("{archive_prefix}: archive missing on source"); }; @@ -596,14 +596,14 @@ async fn pull_single_archive<'a>( } } let source_path = if crypt_config.is_some() { - if let Err(err) = std::fs::remove_file(&tmp_path) { + if let Err(err) = tokio::fs::remove_file(&tmp_path).await { bail!("{archive_prefix}: Failed to remove temp. file {tmp_path:?} failed - {err}"); } tmp_dec_path } else { tmp_path }; - if let Err(err) = std::fs::rename(&source_path, &path) { + if let Err(err) = tokio::fs::rename(&source_path, &path).await { bail!("{archive_prefix}: Atomic rename file {path:?} failed - {err}"); } @@ -662,7 +662,7 @@ async fn pull_snapshot<'a>( .await .with_context(|| prefix.clone())? else { - let _ = std::fs::remove_file(&tmp_manifest_name); + let _ = tokio::fs::remove_file(&tmp_manifest_name).await; log_sender .log( Level::INFO, @@ -713,7 +713,7 @@ async fn pull_snapshot<'a>( log_sender .log(Level::INFO, format!("{prefix}: no data changes")) .await?; - let _ = std::fs::remove_file(&tmp_manifest_name); + let _ = tokio::fs::remove_file(&tmp_manifest_name).await; Ok::<(), Error>(()) }; @@ -752,7 +752,7 @@ async fn pull_snapshot<'a>( params.verified_only, params.encrypted_only, ) { - let _ = std::fs::remove_file(&tmp_manifest_name); + let _ = tokio::fs::remove_file(&tmp_manifest_name).await; log_sender .log( Level::INFO, @@ -893,7 +893,7 @@ async fn pull_snapshot<'a>( nix::unistd::fsync(tmp_manifest_file.as_raw_fd())?; } - if let Err(err) = std::fs::rename(&tmp_manifest_name, &manifest_name) { + if let Err(err) = tokio::fs::rename(&tmp_manifest_name, &manifest_name).await { bail!("{prefix}: Atomic rename file {manifest_name:?} failed - {err}"); } if let DatastoreBackend::S3(s3_client) = backend { @@ -913,8 +913,9 @@ async fn pull_snapshot<'a>( fetch_log(crypt_config).await?; - snapshot - .cleanup_unreferenced_files(&manifest) + let snapshot = snapshot.clone(); + tokio::task::spawn_blocking(move || snapshot.cleanup_unreferenced_files(&manifest)) + .await? .map_err(|err| format_err!("{prefix}: failed to cleanup unreferenced files - {err}"))?; Ok(Some(sync_stats)) -- 2.47.3