fix: move image processing off async runtime with spawn_blocking
CPU-intensive image operations (decode, Lanczos3 resize, JPEG encode) were running directly on the async worker thread, blocking all other requests for 100-500ms per upload. Wrap in spawn_blocking in both upload_image() and save_deferred_image().
This commit is contained in:
parent
cf56ac28f7
commit
082b582cb3
1 changed files with 22 additions and 14 deletions
|
|
@ -105,7 +105,10 @@ pub(crate) async fn upload_image(
|
||||||
|
|
||||||
let (upload, _source) = payload.into_parts();
|
let (upload, _source) = payload.into_parts();
|
||||||
|
|
||||||
let processed = process_data_url(&upload.image)
|
let image_data = upload.image;
|
||||||
|
let processed = tokio::task::spawn_blocking(move || process_data_url(&image_data))
|
||||||
|
.await
|
||||||
|
.map_err(|e| AppError::unexpected(format!("image processing task failed: {e}")))?
|
||||||
.map_err(|e| AppError::validation(format!("invalid image: {e}")))?;
|
.map_err(|e| AppError::validation(format!("invalid image: {e}")))?;
|
||||||
|
|
||||||
let image = EntityImage {
|
let image = EntityImage {
|
||||||
|
|
@ -232,8 +235,18 @@ pub(crate) async fn save_deferred_image(
|
||||||
let Some(data_url) = data_url.filter(|s| !s.is_empty()) else {
|
let Some(data_url) = data_url.filter(|s| !s.is_empty()) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
match process_data_url(data_url) {
|
let data_url = data_url.to_string();
|
||||||
Ok(processed) => {
|
let processed = match tokio::task::spawn_blocking(move || process_data_url(&data_url)).await {
|
||||||
|
Ok(Ok(p)) => p,
|
||||||
|
Ok(Err(err)) => {
|
||||||
|
tracing::warn!(entity_type, entity_id, error = %err, "failed to process deferred image");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
tracing::warn!(entity_type, entity_id, error = %err, "deferred image task panicked");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
let image = EntityImage {
|
let image = EntityImage {
|
||||||
entity_type: entity_type.to_string(),
|
entity_type: entity_type.to_string(),
|
||||||
entity_id,
|
entity_id,
|
||||||
|
|
@ -245,11 +258,6 @@ pub(crate) async fn save_deferred_image(
|
||||||
tracing::warn!(entity_type, entity_id, error = %err, "failed to save deferred image");
|
tracing::warn!(entity_type, entity_id, error = %err, "failed to save deferred image");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(err) => {
|
|
||||||
tracing::warn!(entity_type, entity_id, error = %err, "failed to process deferred image");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn image_response(data: Vec<u8>, content_type: &str) -> Response {
|
fn image_response(data: Vec<u8>, content_type: &str) -> Response {
|
||||||
Response::builder()
|
Response::builder()
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue