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:
@@ -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()),
|
||||||
|
)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user