perf: replace blocking std::fs calls with tokio::fs::metadata in PathService

file_exists(), directory_exists(), and ensure_directory() were using
synchronous Path::exists() + is_file()/is_dir() which each perform two
blocking stat(2) syscalls on the Tokio worker thread (0.2-4ms total).

Replace with a single tokio::fs::metadata().await call per method:
- Async: worker thread is never blocked
- 1 syscall instead of 2: metadata() returns file type in one stat(2)
- Proper error propagation for I/O errors (not just silent false)
This commit is contained in:
Dionisio
2026-02-23 22:56:20 +01:00
parent b5652b029d
commit 958836e96b
+39 -12
View File
@@ -133,8 +133,20 @@ impl StoragePort for PathService {
// Resolve to physical path // Resolve to physical path
let physical_path = self.resolve_path(storage_path); let physical_path = self.resolve_path(storage_path);
// Create directories if they don't exist // Check current state with a single async stat() — no worker blocking.
if !physical_path.exists() { match fs::metadata(&physical_path).await {
Ok(meta) if meta.is_dir() => { /* already exists */ }
Ok(_) => {
return Err(DomainError::new(
ErrorKind::InvalidInput,
"Storage",
format!(
"Path exists but is not a directory: {}",
physical_path.display()
),
));
}
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
fs::create_dir_all(&physical_path).await.map_err(|e| { fs::create_dir_all(&physical_path).await.map_err(|e| {
DomainError::new( DomainError::new(
ErrorKind::AccessDenied, ErrorKind::AccessDenied,
@@ -145,16 +157,15 @@ impl StoragePort for PathService {
})?; })?;
tracing::debug!("Created directory: {}", physical_path.display()); tracing::debug!("Created directory: {}", physical_path.display());
} else if !physical_path.is_dir() { }
Err(e) => {
return Err(DomainError::new( return Err(DomainError::new(
ErrorKind::InvalidInput, ErrorKind::InternalError,
"Storage", "Storage",
format!( format!("Cannot stat {}: {e}", physical_path.display()),
"Path exists but is not a directory: {}",
physical_path.display()
),
)); ));
} }
}
Ok(()) Ok(())
} }
@@ -162,15 +173,31 @@ impl StoragePort for PathService {
async fn file_exists(&self, storage_path: &StoragePath) -> Result<bool, DomainError> { async fn file_exists(&self, storage_path: &StoragePath) -> Result<bool, DomainError> {
let physical_path = self.resolve_path(storage_path); let physical_path = self.resolve_path(storage_path);
let exists = physical_path.exists() && physical_path.is_file(); // Single async stat() — no worker blocking, one syscall instead of two.
Ok(exists) match fs::metadata(&physical_path).await {
Ok(meta) => Ok(meta.is_file()),
Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(false),
Err(e) => Err(DomainError::new(
ErrorKind::InternalError,
"Storage",
format!("Cannot stat {}: {e}", physical_path.display()),
)),
}
} }
async fn directory_exists(&self, storage_path: &StoragePath) -> Result<bool, DomainError> { async fn directory_exists(&self, storage_path: &StoragePath) -> Result<bool, DomainError> {
let physical_path = self.resolve_path(storage_path); let physical_path = self.resolve_path(storage_path);
let exists = physical_path.exists() && physical_path.is_dir(); // Single async stat() — no worker blocking, one syscall instead of two.
Ok(exists) match fs::metadata(&physical_path).await {
Ok(meta) => Ok(meta.is_dir()),
Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(false),
Err(e) => Err(DomainError::new(
ErrorKind::InternalError,
"Storage",
format!("Cannot stat {}: {e}", physical_path.display()),
)),
}
} }
} }