improve performance of community followers inbox query (#3482)
authorphiresky <phireskyde+git@gmail.com>
Wed, 5 Jul 2023 15:50:26 +0000 (17:50 +0200)
committerGitHub <noreply@github.com>
Wed, 5 Jul 2023 15:50:26 +0000 (11:50 -0400)
* improve performance of community followers inbox query

* nightly format

* force woodpecker to retry

---------

Co-authored-by: Dessalines <dessalines@users.noreply.github.com>
crates/apub/src/objects/community.rs
crates/apub/src/protocol/collections/group_followers.rs
crates/db_views_actor/src/community_follower_view.rs

index 672b25ebf9e7942ed25b7ef6e3a4c77b87545a7e..113fa6c009cad61e972e31e9e1a72db75983bad3 100644 (file)
@@ -14,7 +14,6 @@ use activitypub_federation::{
   traits::{Actor, Object},
 };
 use chrono::NaiveDateTime;
-use itertools::Itertools;
 use lemmy_api_common::{
   context::LemmyContext,
   utils::{generate_featured_url, generate_moderators_url, generate_outbox_url},
@@ -188,17 +187,11 @@ impl ApubCommunity {
     let id = self.id;
 
     let local_site_data = local_site_data_cached(context.pool()).await?;
-    let follows = CommunityFollowerView::for_community(context.pool(), id).await?;
+    let follows = CommunityFollowerView::get_community_follower_inboxes(context.pool(), id).await?;
+
     let inboxes: Vec<Url> = follows
       .into_iter()
-      .filter(|f| !f.follower.local)
-      .map(|f| {
-        f.follower
-          .shared_inbox_url
-          .unwrap_or(f.follower.inbox_url)
-          .into()
-      })
-      .unique()
+      .map(Into::into)
       .filter(|inbox: &Url| inbox.host_str() != Some(&context.settings().hostname))
       // Don't send to blocked instances
       .filter(|inbox| check_apub_id_valid(inbox, &local_site_data).is_ok())
index c7df8df2b245f34b5a33553efae29a79653cbc80..964bbdf82d141a0ff67de3249d44e8fac04de8df 100644 (file)
@@ -22,12 +22,12 @@ impl GroupFollowers {
   ) -> Result<GroupFollowers, LemmyError> {
     let community_id = community.id;
     let community_followers =
-      CommunityFollowerView::for_community(context.pool(), community_id).await?;
+      CommunityFollowerView::count_community_followers(context.pool(), community_id).await?;
 
     Ok(GroupFollowers {
       id: generate_followers_url(&community.actor_id)?.into(),
       r#type: CollectionType::Collection,
-      total_items: community_followers.len() as i32,
+      total_items: community_followers as i32,
       items: vec![],
     })
   }
index ccc7ae706790f7df35a6ad19beb4d42905293b15..11baddc0e7fc1035c5520497addab1e5f0147e5b 100644 (file)
@@ -1,8 +1,14 @@
 use crate::structs::CommunityFollowerView;
-use diesel::{result::Error, ExpressionMethods, QueryDsl};
+use diesel::{
+  dsl::{count_star, not},
+  result::Error,
+  sql_function,
+  ExpressionMethods,
+  QueryDsl,
+};
 use diesel_async::RunQueryDsl;
 use lemmy_db_schema::{
-  newtypes::{CommunityId, PersonId},
+  newtypes::{CommunityId, DbUrl, PersonId},
   schema::{community, community_follower, person},
   source::{community::Community, person::Person},
   traits::JoinView,
@@ -11,19 +17,37 @@ use lemmy_db_schema::{
 
 type CommunityFollowerViewTuple = (Community, Person);
 
+sql_function!(fn coalesce(x: diesel::sql_types::Nullable<diesel::sql_types::Text>, y: diesel::sql_types::Text) -> diesel::sql_types::Text);
+
 impl CommunityFollowerView {
-  pub async fn for_community(pool: &DbPool, community_id: CommunityId) -> Result<Vec<Self>, Error> {
+  pub async fn get_community_follower_inboxes(
+    pool: &DbPool,
+    community_id: CommunityId,
+  ) -> Result<Vec<DbUrl>, Error> {
     let conn = &mut get_conn(pool).await?;
     let res = community_follower::table
-      .inner_join(community::table)
+      .filter(community_follower::community_id.eq(community_id))
+      .filter(not(person::local))
       .inner_join(person::table)
-      .select((community::all_columns, person::all_columns))
+      .select(coalesce(person::shared_inbox_url, person::inbox_url))
+      .distinct()
+      .load::<DbUrl>(conn)
+      .await?;
+
+    Ok(res)
+  }
+  pub async fn count_community_followers(
+    pool: &DbPool,
+    community_id: CommunityId,
+  ) -> Result<i64, Error> {
+    let conn = &mut get_conn(pool).await?;
+    let res = community_follower::table
       .filter(community_follower::community_id.eq(community_id))
-      .order_by(community::title)
-      .load::<CommunityFollowerViewTuple>(conn)
+      .select(count_star())
+      .first::<i64>(conn)
       .await?;
 
-    Ok(res.into_iter().map(Self::from_tuple).collect())
+    Ok(res)
   }
 
   pub async fn for_person(pool: &DbPool, person_id: PersonId) -> Result<Vec<Self>, Error> {