perf: findings 6.1, 6.2, 2.6 — async Argon2, moka cache, full streaming migration

- 6.1: PasswordHasherPort now async_trait with spawn_blocking for Argon2
- 6.2: OIDC pending maps migrated from std::sync::Mutex to moka::sync::Cache with TTL
- 2.6: All file download paths migrated to 64KB streaming (get_file_stream / read_blob_stream)
  - WOPI, dedup, batch ZIP, file_retrieval_service consumers migrated
  - WebDAV COPY uses zero-copy dedup (copy_file)
  - Removed dead code: get_file_content, get_file_mmap, read_blob, read_blob_bytes
    from traits, impls, stubs, and mocks (18 files touched)
This commit is contained in:
Diocrafts
2026-02-23 00:51:46 +01:00
parent b501c4052b
commit 85908311dc
18 changed files with 235 additions and 305 deletions
+17 -4
View File
@@ -313,13 +313,26 @@ impl DedupHandler {
.and_then(|m| m.content_type.clone())
.unwrap_or_else(|| "application/octet-stream".to_string());
match dedup.read_blob_bytes(&hash).await {
Ok(content) => Response::builder()
// Stream blob in 64 KB chunks — constant memory regardless of size
let size = match dedup.blob_size(&hash).await {
Ok(s) => s,
Err(_) => {
return Response::builder()
.status(StatusCode::NOT_FOUND)
.header(header::CONTENT_TYPE, "application/json")
.body(Body::from(r#"{"error": "Blob not found"}"#))
.unwrap()
.into_response();
}
};
match dedup.read_blob_stream(&hash).await {
Ok(stream) => Response::builder()
.status(StatusCode::OK)
.header(header::CONTENT_TYPE, content_type)
.header(header::CONTENT_LENGTH, content.len().to_string())
.header(header::CONTENT_LENGTH, size.to_string())
.header("X-Dedup-Hash", &hash)
.body(Body::from(content))
.body(Body::from_stream(stream))
.unwrap()
.into_response(),
Err(_) => Response::builder()
+13 -15
View File
@@ -936,7 +936,7 @@ async fn handle_copy(
// Get services from state
let file_retrieval_service = &state.applications.file_retrieval_service;
let file_upload_service = &state.applications.file_upload_service;
let _file_upload_service = &state.applications.file_upload_service;
let folder_service = &state.applications.folder_service;
// Check if destination already exists (for Overwrite header compliance)
@@ -996,26 +996,24 @@ async fn handle_copy(
})?;
if recursive {
// Copy subfolders and files (simplified implementation)
// Copy files via zero-copy dedup (only increments blob ref_count)
let files = file_retrieval_service
.list_files(Some(&folder.id))
.await
.map_err(|e| AppError::internal_error(format!("Failed to list files: {}", e)))?;
let file_management_service = &state.applications.file_management_service;
let new_folder_id = Some(_new_folder.id.clone());
for file in files {
// Get file content
if let Ok(content) = file_retrieval_service.get_file_content(&file.id).await {
// Create new file in destination
file_upload_service
.create_file(&destination_path, &file.name, &content, &file.mime_type)
.await
.map_err(|e| {
AppError::internal_error(format!(
"Failed to copy file {}: {}",
file.name, e
))
})?;
}
file_management_service
.copy_file(&file.id, new_folder_id.clone())
.await
.map_err(|e| {
AppError::internal_error(format!(
"Failed to copy file {}: {}",
file.name, e
))
})?;
}
}
} else {
+8 -2
View File
@@ -117,6 +117,9 @@ async fn check_file_info(
}
/// GET /wopi/files/{file_id}/contents — GetFile
///
/// Streams the file content to Collabora/OnlyOffice in 64 KB chunks.
/// Memory usage is constant (~64 KB) regardless of file size.
async fn get_file(
Path(file_id): Path<String>,
Query(token_query): Query<WopiTokenQuery>,
@@ -138,10 +141,13 @@ async fn get_file(
.app_state
.applications
.file_retrieval_service
.get_file_content(&file_id)
.get_file_stream(&file_id)
.await
{
Ok(content) => (StatusCode::OK, content).into_response(),
Ok(stream) => {
let body = axum::body::Body::from_stream(std::pin::Pin::from(stream));
(StatusCode::OK, body).into_response()
}
Err(_) => StatusCode::NOT_FOUND.into_response(),
}
}