From 1fdb08b493ab04ce9035944d8d664bb9dc216491 Mon Sep 17 00:00:00 2001 From: Andrew Phillips Date: Mon, 25 Aug 2025 20:55:42 -0300 Subject: [PATCH] feat: optimize item content streaming to reduce redundant database calls Co-authored-by: aider (openai/andrew/openrouter/deepseek/deepseek-chat-v3.1) --- src/modes/server/api/item.rs | 27 +++++---- src/services/async_item_service.rs | 91 +++++++++++++++++++++++++++++- 2 files changed, 104 insertions(+), 14 deletions(-) diff --git a/src/modes/server/api/item.rs b/src/modes/server/api/item.rs index 75863f4..c8de022 100644 --- a/src/modes/server/api/item.rs +++ b/src/modes/server/api/item.rs @@ -232,7 +232,7 @@ async fn stream_item_content_response( offset: u64, length: u64, ) -> Result { - // First get the item metadata to check if it's binary and get MIME type + // Get the item with metadata once let item_with_meta = item_service.get_item(item_id).await.map_err(|e| { warn!("Failed to get item {} for content: {}", item_id, e); StatusCode::INTERNAL_SERVER_ERROR @@ -246,21 +246,26 @@ async fn stream_item_content_response( // Check if content is binary when allow_binary is false if !allow_binary { - match item_service.get_item_content_info(item_id).await { - Ok((_, _, is_binary)) => { - if is_binary { - return Err(StatusCode::BAD_REQUEST); + let is_binary = if let Some(binary_val) = metadata.get("binary") { + binary_val == "true" + } else { + // If binary metadata isn't set, we need to check the content + match item_service.get_item_content_info(item_id).await { + Ok((_, _, is_binary)) => is_binary, + Err(e) => { + warn!("Failed to get content info for binary check for item {}: {}", item_id, e); + return Err(StatusCode::INTERNAL_SERVER_ERROR); } } - Err(e) => { - warn!("Failed to get content info for binary check for item {}: {}", item_id, e); - return Err(StatusCode::INTERNAL_SERVER_ERROR); - } + }; + + if is_binary { + return Err(StatusCode::BAD_REQUEST); } } - // Now stream the content with the specified offset and length - match item_service.stream_item_content_by_id(item_id, true, offset, length).await { + // Now stream the content with the specified offset and length using pre-fetched metadata + match item_service.stream_item_content_by_id_with_metadata(item_id, &metadata, true, offset, length).await { Ok((stream, _)) => { let body = axum::body::Body::from_stream(stream); let response = Response::builder() diff --git a/src/services/async_item_service.rs b/src/services/async_item_service.rs index 21522de..392a95d 100644 --- a/src/services/async_item_service.rs +++ b/src/services/async_item_service.rs @@ -70,14 +70,99 @@ impl AsyncItemService { let db = self.db.clone(); let item_service = self.item_service.clone(); - // Get item content, mime type, and binary status - let (content, mime_type, is_binary) = tokio::task::spawn_blocking(move || { + // Get item content + let content = tokio::task::spawn_blocking(move || { let conn = db.blocking_lock(); - item_service.get_item_content_info(&conn, item_id) + let item_with_content = item_service.get_item_content(&conn, item_id)?; + Ok::<_, CoreError>(item_with_content.content) }) .await .unwrap()?; + // Get metadata to determine MIME type and binary status + let (mime_type, is_binary) = { + let db = self.db.clone(); + let item_service = self.item_service.clone(); + tokio::task::spawn_blocking(move || { + let conn = db.blocking_lock(); + let item_with_meta = item_service.get_item(&conn, item_id)?; + let metadata = item_with_meta.meta_as_map(); + + let mime_type = metadata + .get("mime_type") + .map(|s| s.to_string()) + .unwrap_or_else(|| "application/octet-stream".to_string()); + + let is_binary = if let Some(binary_val) = metadata.get("binary") { + binary_val == "true" + } else { + crate::common::is_binary::is_binary(&content) + }; + + Ok::<_, CoreError>((mime_type, is_binary)) + }) + .await + .unwrap()? + }; + + // Check if content is binary when allow_binary is false + if !allow_binary && is_binary { + return Err(CoreError::InvalidInput("Binary content not allowed".to_string())); + } + + // Create a stream that reads only the requested portion + let content_len = content.len() as u64; + + // Apply offset and length constraints + let start = std::cmp::min(offset, content_len); + let end = if length > 0 { + std::cmp::min(start + length, content_len) + } else { + content_len + }; + + let stream = if start < content_len { + let chunk = tokio_util::bytes::Bytes::from(content[start as usize..end as usize].to_vec()); + Box::pin(tokio_stream::iter(vec![Ok(chunk)])) + } else { + Box::pin(tokio_stream::iter(vec![])) + }; + + Ok((stream, mime_type)) + } + + pub async fn stream_item_content_by_id_with_metadata( + &self, + item_id: i64, + metadata: &HashMap, + allow_binary: bool, + offset: u64, + length: u64, + ) -> Result<(std::pin::Pin> + Send>>, String), CoreError> { + let db = self.db.clone(); + let item_service = self.item_service.clone(); + + // Get item content + let content = tokio::task::spawn_blocking(move || { + let conn = db.blocking_lock(); + let item_with_content = item_service.get_item_content(&conn, item_id)?; + Ok::<_, CoreError>(item_with_content.content) + }) + .await + .unwrap()?; + + // Use provided metadata to determine MIME type and binary status + let mime_type = metadata + .get("mime_type") + .map(|s| s.to_string()) + .unwrap_or_else(|| "application/octet-stream".to_string()); + + let is_binary = if let Some(binary_val) = metadata.get("binary") { + binary_val == "true" + } else { + crate::common::is_binary::is_binary(&content) + }; + // Check if content is binary when allow_binary is false if !allow_binary && is_binary { return Err(CoreError::InvalidInput("Binary content not allowed".to_string()));