From 345ada27c08bf4b7090ec6f04c19aaf1642fd349 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois-Xavier=20Talbot?= <108630700+fetchfern@users.noreply.github.com> Date: Mon, 2 Feb 2026 08:27:29 -0500 Subject: [PATCH] Increase/make configurable search timeouts (#5261) * Increase default operation timeout, make it configurable via SEARCH_OPERATION_TIMEOUT * Don't update index settings when unneeded * Add to env local --- apps/labrinth/.env.local | 2 + apps/labrinth/src/search/indexing/mod.rs | 92 ++++++++++++++++-------- 2 files changed, 66 insertions(+), 28 deletions(-) diff --git a/apps/labrinth/.env.local b/apps/labrinth/.env.local index ef9887a0c..9d32888c7 100644 --- a/apps/labrinth/.env.local +++ b/apps/labrinth/.env.local @@ -17,6 +17,8 @@ DATABASE_MAX_CONNECTIONS=16 MEILISEARCH_READ_ADDR=http://localhost:7700 MEILISEARCH_WRITE_ADDRS=http://localhost:7700 +# 5 minutes in milliseconds +SEARCH_OPERATION_TIMEOUT=300000 # # For a sharded Meilisearch setup (sharded-meilisearch docker compose profile) # MEILISEARCH_READ_ADDR=http://localhost:7710 diff --git a/apps/labrinth/src/search/indexing/mod.rs b/apps/labrinth/src/search/indexing/mod.rs index 2b12e0ecc..2da327ce1 100644 --- a/apps/labrinth/src/search/indexing/mod.rs +++ b/apps/labrinth/src/search/indexing/mod.rs @@ -39,14 +39,22 @@ pub enum IndexingError { // // Set this to 50k for better observability const MEILISEARCH_CHUNK_SIZE: usize = 50000; // 10_000_000 -const TIMEOUT: std::time::Duration = std::time::Duration::from_secs(120); + +fn search_operation_timeout() -> std::time::Duration { + let default_ms = 5 * 60 * 1000; // 5 minutes + let ms = dotenvy::var("SEARCH_OPERATION_TIMEOUT") + .ok() + .and_then(|v| v.parse::().ok()) + .unwrap_or(default_ms); + std::time::Duration::from_millis(ms) +} pub async fn remove_documents( ids: &[crate::models::ids::VersionId], config: &SearchConfig, ) -> Result<(), IndexingError> { - let mut indexes = get_indexes_for_indexing(config, false).await?; - let indexes_next = get_indexes_for_indexing(config, true).await?; + let mut indexes = get_indexes_for_indexing(config, false, false).await?; + let indexes_next = get_indexes_for_indexing(config, true, false).await?; for list in &mut indexes { for alt_list in &indexes_next { @@ -94,11 +102,11 @@ pub async fn index_projects( info!("Ensuring current indexes exists"); // First, ensure current index exists (so no error happens- current index should be worst-case empty, not missing) - get_indexes_for_indexing(config, false).await?; + get_indexes_for_indexing(config, false, false).await?; info!("Deleting surplus indexes"); // Then, delete the next index if it still exists - let indices = get_indexes_for_indexing(config, true).await?; + let indices = get_indexes_for_indexing(config, true, false).await?; for client_indices in indices { for index in client_indices { index.delete().await?; @@ -107,7 +115,7 @@ pub async fn index_projects( info!("Recreating next index"); // Recreate the next index for indexing - let indices = get_indexes_for_indexing(config, true).await?; + let indices = get_indexes_for_indexing(config, true, true).await?; let all_loader_fields = crate::database::models::loader_fields::LoaderField::get_fields_all( @@ -208,6 +216,7 @@ pub async fn swap_index( pub async fn get_indexes_for_indexing( config: &SearchConfig, next: bool, // Get the 'next' one + update_settings: bool, ) -> Result>, IndexingError> { let client = config.make_batch_client()?; let project_name = config.get_index_name("projects", next); @@ -230,6 +239,7 @@ pub async fn get_indexes_for_indexing( "exactness", "sort", ]), + update_settings, ) .await?; let projects_filtered_index = create_or_update_index( @@ -243,6 +253,7 @@ pub async fn get_indexes_for_indexing( "attribute", "exactness", ]), + update_settings, ) .await?; @@ -258,6 +269,7 @@ async fn create_or_update_index( client: &Client, name: &str, custom_rules: Option<&'static [&'static str]>, + update_settings: bool, ) -> Result { info!("Updating/creating index"); @@ -271,16 +283,26 @@ async fn create_or_update_index( settings = settings.with_ranking_rules(custom_rules); } - info!("Performing index settings set."); - index - .set_settings(&settings) - .await - .inspect_err(|e| error!("Error setting index settings: {e:?}"))? - .wait_for_completion(client, None, Some(TIMEOUT)) - .await - .inspect_err(|e| { - error!("Error setting index settings while waiting: {e:?}") - })?; + if update_settings { + info!("Updating index settings"); + index + .set_settings(&settings) + .await + .inspect_err(|e| { + error!("Error setting index settings: {e:?}") + })? + .wait_for_completion( + client, + None, + Some(search_operation_timeout()), + ) + .await + .inspect_err(|e| { + error!( + "Error setting index settings while waiting: {e:?}" + ) + })?; + } info!("Done performing index settings set."); Ok(index) @@ -291,7 +313,11 @@ async fn create_or_update_index( // Only create index and set settings if the index doesn't already exist let task = client.create_index(name, Some("version_id")).await?; let task = task - .wait_for_completion(client, None, Some(TIMEOUT)) + .wait_for_completion( + client, + None, + Some(search_operation_timeout()), + ) .await .inspect_err(|e| { error!("Error creating index while waiting: {e:?}") @@ -306,15 +332,25 @@ async fn create_or_update_index( settings = settings.with_ranking_rules(custom_rules); } - index - .set_settings(&settings) - .await - .inspect_err(|e| error!("Error setting index settings: {e:?}"))? - .wait_for_completion(client, None, Some(TIMEOUT)) - .await - .inspect_err(|e| { - error!("Error setting index settings while waiting: {e:?}") - })?; + if update_settings { + index + .set_settings(&settings) + .await + .inspect_err(|e| { + error!("Error setting index settings: {e:?}") + })? + .wait_for_completion( + client, + None, + Some(search_operation_timeout()), + ) + .await + .inspect_err(|e| { + error!( + "Error setting index settings while waiting: {e:?}" + ) + })?; + } Ok(index) } @@ -436,10 +472,10 @@ async fn update_and_add_to_index( // // Allow a long timeout for adding new attributes- it only needs to happen the once // filterable_task - // .wait_for_completion(client, None, Some(TIMEOUT * 100)) + // .wait_for_completion(client, None, Some(search_operation_timeout() * 100)) // .await?; // displayable_task - // .wait_for_completion(client, None, Some(TIMEOUT * 100)) + // .wait_for_completion(client, None, Some(search_operation_timeout() * 100)) // .await?; // }