perf(dedup): remove blocking std::fs calls from async context
Two fixes in dedup_service.rs: 1. Rename error path (L234): std::fs::remove_file → tokio::fs::remove_file Restructured from map_err closure to match block since .await cannot be used inside a sync closure. 2. Integrity verify (L691): fused blocking .exists() + async metadata() into a single fs::metadata().await call. Eliminates one stat() syscall per blob AND removes the only remaining blocking I/O from the verify_integrity hot loop (buffer_unordered × VERIFY_CONCURRENCY).
This commit is contained in:
@@ -231,10 +231,14 @@ impl DedupService {
|
||||
DomainError::internal_error("Dedup", format!("Failed to write temp blob: {}", e))
|
||||
})?;
|
||||
|
||||
fs::rename(&temp_path, &blob_path).await.map_err(|e| {
|
||||
let _ = std::fs::remove_file(&temp_path);
|
||||
DomainError::internal_error("Dedup", format!("Failed to move blob: {}", e))
|
||||
})?;
|
||||
if let Err(e) = fs::rename(&temp_path, &blob_path).await {
|
||||
// Clean up temp file asynchronously (never block the Tokio worker)
|
||||
let _ = fs::remove_file(&temp_path).await;
|
||||
return Err(DomainError::internal_error(
|
||||
"Dedup",
|
||||
format!("Failed to move blob: {}", e),
|
||||
));
|
||||
}
|
||||
|
||||
// Register in PostgreSQL (ON CONFLICT handles rare race with another writer)
|
||||
sqlx::query(
|
||||
@@ -687,10 +691,29 @@ impl DedupService {
|
||||
|
||||
let mut issues = Vec::new();
|
||||
|
||||
// Check file exists
|
||||
if !blob_path.exists() {
|
||||
issues.push(format!("{}: file missing on disk", hash));
|
||||
return issues;
|
||||
// Single async metadata() replaces the previous
|
||||
// blocking .exists() + separate metadata() — one
|
||||
// stat() syscall instead of two, and non-blocking.
|
||||
let file_meta = match fs::metadata(&blob_path).await {
|
||||
Ok(m) => m,
|
||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
|
||||
issues.push(format!("{}: file missing on disk", hash));
|
||||
return issues;
|
||||
}
|
||||
Err(e) => {
|
||||
issues.push(format!("{}: metadata error ({})", hash, e));
|
||||
return issues;
|
||||
}
|
||||
};
|
||||
|
||||
// Check size
|
||||
if file_meta.len() != expected_size as u64 {
|
||||
issues.push(format!(
|
||||
"{}: size mismatch (expected: {}, actual: {})",
|
||||
hash,
|
||||
expected_size,
|
||||
file_meta.len(),
|
||||
));
|
||||
}
|
||||
|
||||
// Verify hash
|
||||
@@ -708,18 +731,6 @@ impl DedupService {
|
||||
}
|
||||
}
|
||||
|
||||
// Check size
|
||||
if let Ok(file_meta) = fs::metadata(&blob_path).await
|
||||
&& file_meta.len() != expected_size as u64
|
||||
{
|
||||
issues.push(format!(
|
||||
"{}: size mismatch (expected: {}, actual: {})",
|
||||
hash,
|
||||
expected_size,
|
||||
file_meta.len(),
|
||||
));
|
||||
}
|
||||
|
||||
issues
|
||||
}
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user