From 30d58865b82421675c8923164ad278685bcb0971 Mon Sep 17 00:00:00 2001 From: Nutomic Date: Tue, 12 Dec 2023 17:56:39 +0100 Subject: [PATCH] Speed up GET /api/v3/site endpoint (#4245) * Make db queries for GET /api/v3/site in parallel (ref #4244) * Cache site response * machete * Use try_join_with_pool macro * machete * taplo * ttl 1s --- Cargo.lock | 4 ++ crates/api_crud/Cargo.toml | 9 ++- crates/api_crud/src/site/read.rs | 97 +++++++++++++++++--------------- crates/utils/translations | 2 +- 4 files changed, 65 insertions(+), 47 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 29296775e..8e6a78a32 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2589,12 +2589,16 @@ version = "0.19.0-rc.13" dependencies = [ "activitypub_federation", "actix-web", + "anyhow", "bcrypt", + "futures", "lemmy_api_common", "lemmy_db_schema", "lemmy_db_views", "lemmy_db_views_actor", "lemmy_utils", + "moka", + "once_cell", "tracing", "url", "uuid", diff --git a/crates/api_crud/Cargo.toml b/crates/api_crud/Cargo.toml index 867c3f215..ea7c81399 100644 --- a/crates/api_crud/Cargo.toml +++ b/crates/api_crud/Cargo.toml @@ -22,5 +22,12 @@ bcrypt = { workspace = true } actix-web = { workspace = true } tracing = { workspace = true } url = { workspace = true } -webmention = "0.5.0" +futures.workspace = true uuid = { workspace = true } +moka.workspace = true +once_cell.workspace = true +anyhow.workspace = true +webmention = "0.5.0" + +[package.metadata.cargo-machete] +ignored = ["futures"] diff --git a/crates/api_crud/src/site/read.rs b/crates/api_crud/src/site/read.rs index aceee29d4..c340ce44b 100644 --- a/crates/api_crud/src/site/read.rs +++ b/crates/api_crud/src/site/read.rs @@ -21,46 +21,68 @@ use lemmy_utils::{ error::{LemmyError, LemmyErrorExt, LemmyErrorType}, version, }; +use moka::future::Cache; +use once_cell::sync::Lazy; +use std::time::Duration; #[tracing::instrument(skip(context))] pub async fn get_site( local_user_view: Option, context: Data, ) -> Result, LemmyError> { - let site_view = SiteView::read_local(&mut context.pool()).await?; + static CACHE: Lazy> = Lazy::new(|| { + Cache::builder() + .max_capacity(1) + .time_to_live(Duration::from_secs(1)) + .build() + }); - let admins = PersonView::admins(&mut context.pool()).await?; + // This data is independent from the user account so we can cache it across requests + let mut site_response = CACHE + .try_get_with::<_, LemmyError>((), async { + let site_view = SiteView::read_local(&mut context.pool()).await?; + let admins = PersonView::admins(&mut context.pool()).await?; + let all_languages = Language::read_all(&mut context.pool()).await?; + let discussion_languages = SiteLanguage::read_local_raw(&mut context.pool()).await?; + let taglines = Tagline::get_all(&mut context.pool(), site_view.local_site.id).await?; + let custom_emojis = + CustomEmojiView::get_all(&mut context.pool(), site_view.local_site.id).await?; + Ok(GetSiteResponse { + site_view, + admins, + version: version::VERSION.to_string(), + my_user: None, + all_languages, + discussion_languages, + taglines, + custom_emojis, + }) + }) + .await + .map_err(|e| anyhow::anyhow!("Failed to construct site response: {e}"))?; - // Build the local user - let my_user = if let Some(local_user_view) = local_user_view { + // Build the local user with parallel queries and add it to site response + site_response.my_user = if let Some(local_user_view) = local_user_view { let person_id = local_user_view.person.id; let local_user_id = local_user_view.local_user.id; + let pool = &mut context.pool(); - let follows = CommunityFollowerView::for_person(&mut context.pool(), person_id) - .await - .with_lemmy_type(LemmyErrorType::SystemErrLogin)?; - - let person_id = local_user_view.person.id; - let community_blocks = CommunityBlockView::for_person(&mut context.pool(), person_id) - .await - .with_lemmy_type(LemmyErrorType::SystemErrLogin)?; - - let instance_blocks = InstanceBlockView::for_person(&mut context.pool(), person_id) - .await - .with_lemmy_type(LemmyErrorType::SystemErrLogin)?; - - let person_id = local_user_view.person.id; - let person_blocks = PersonBlockView::for_person(&mut context.pool(), person_id) - .await - .with_lemmy_type(LemmyErrorType::SystemErrLogin)?; - - let moderates = CommunityModeratorView::for_person(&mut context.pool(), person_id) - .await - .with_lemmy_type(LemmyErrorType::SystemErrLogin)?; - - let discussion_languages = LocalUserLanguage::read(&mut context.pool(), local_user_id) - .await - .with_lemmy_type(LemmyErrorType::SystemErrLogin)?; + let ( + follows, + community_blocks, + instance_blocks, + person_blocks, + moderates, + discussion_languages, + ) = lemmy_db_schema::try_join_with_pool!(pool => ( + |pool| CommunityFollowerView::for_person(pool, person_id), + |pool| CommunityBlockView::for_person(pool, person_id), + |pool| InstanceBlockView::for_person(pool, person_id), + |pool| PersonBlockView::for_person(pool, person_id), + |pool| CommunityModeratorView::for_person(pool, person_id), + |pool| LocalUserLanguage::read(pool, local_user_id) + )) + .with_lemmy_type(LemmyErrorType::SystemErrLogin)?; Some(MyUserInfo { local_user_view, @@ -75,20 +97,5 @@ pub async fn get_site( None }; - let all_languages = Language::read_all(&mut context.pool()).await?; - let discussion_languages = SiteLanguage::read_local_raw(&mut context.pool()).await?; - let taglines = Tagline::get_all(&mut context.pool(), site_view.local_site.id).await?; - let custom_emojis = - CustomEmojiView::get_all(&mut context.pool(), site_view.local_site.id).await?; - - Ok(Json(GetSiteResponse { - site_view, - admins, - version: version::VERSION.to_string(), - my_user, - all_languages, - discussion_languages, - taglines, - custom_emojis, - })) + Ok(Json(site_response)) } diff --git a/crates/utils/translations b/crates/utils/translations index aa9438c49..b3343aef7 160000 --- a/crates/utils/translations +++ b/crates/utils/translations @@ -1 +1 @@ -Subproject commit aa9438c4930deb23ae0dc54a061ed4b0b3824582 +Subproject commit b3343aef72e5a7e5df34cf328b910ed798027270