From 163e68243f86f57ffae593b8847768cfa9833c90 Mon Sep 17 00:00:00 2001 From: Timothy Quilling Date: Fri, 12 Dec 2025 00:58:05 +0000 Subject: [PATCH] fix: use cache for post hydration --- parakeet/src/hydration/posts/feed.rs | 2 +- parakeet/src/hydration/posts/mod.rs | 37 +++++++++++++++++++++---------------- 2 file(s) changed, 22 insertion(s)(+), 17 deletion(s)(-) diff --git a/parakeet/src/hydration/posts/feed.rs b/parakeet/src/hydration/posts/feed.rs --- a/parakeet/src/hydration/posts/feed.rs +++ b/parakeet/src/hydration/posts/feed.rs @@ -35,7 +35,7 @@ let (mut posts_hyd, profiles_by_id) = tokio::join!( async { let start = std::time::Instant::now(); - let result = self.hydrate_posts_inner(post_uris).await; + let (result, _cache) = self.hydrate_posts_inner(post_uris).await; let elapsed = start.elapsed().as_secs_f64() * 1000.0; tracing::info!(" ├─ Posts hydration: {:.1} ms", elapsed); result diff --git a/parakeet/src/hydration/posts/mod.rs b/parakeet/src/hydration/posts/mod.rs --- a/parakeet/src/hydration/posts/mod.rs +++ b/parakeet/src/hydration/posts/mod.rs @@ -6,7 +6,7 @@ use lexica::app_bsky::actor::ProfileViewBasic; use lexica::app_bsky::feed::{PostView, PostViewerState, ThreadgateView}; -use builders::{build_postview, build_threadgate_view, HydratePostsRet}; +use builders::{build_postview_with_cache, build_threadgate_view, build_threadgate_view_with_cache, HydratePostsRet}; pub use feed::RawFeedItem; @@ -70,6 +70,7 @@ &self, threadgates: Vec, post_did_map: &HashMap<(i32, i64), String>, // (actor_id, rkey) -> DID + actor_cache: &HashMap, ) -> HashMap { let lists = threadgates.iter().fold(Vec::new(), |mut acc, c| { if let Some(lists) = &c.allowed_lists { @@ -97,7 +98,7 @@ Some(( threadgate_uri, - build_threadgate_view(threadgate, post_did, this_lists, self.loaders.post_state.id_cache()), + build_threadgate_view_with_cache(threadgate, post_did, this_lists, self.loaders.post_state.id_cache(), actor_cache), )) }) .collect() @@ -149,7 +150,7 @@ pub(super) async fn hydrate_posts_inner( &self, posts: Vec, - ) -> HashMap { + ) -> (HashMap, HashMap) { let load_start = std::time::Instant::now(); let posts_with_stats = self.loaders.posts.load_many(posts).await; let load_time = load_start.elapsed().as_secs_f64() * 1000.0; @@ -160,7 +161,7 @@ // Pre-fetch actor IDs needed for reply and threadgate construction let reply_actor_ids = Self::collect_reply_actor_ids(&posts_with_stats); - let _reply_actor_cache = if !reply_actor_ids.is_empty() { + let reply_actor_cache = if !reply_actor_ids.is_empty() { let cache_start = std::time::Instant::now(); let actor_data = self.loaders.post_state.id_cache().get_actor_data_many(&reply_actor_ids).await; let cache_time = cache_start.elapsed().as_secs_f64() * 1000.0; @@ -187,9 +188,10 @@ .values() .map(|(post, _, _)| { let author_id = post.post.actor_id; - actor_id_to_did.insert(author_id, post.did.clone()); - post_key_to_did.insert((post.post.actor_id, post.post.rkey), post.did.clone()); - let uri_with_keys = (post.at_uri.clone(), post.post.actor_id, post.post.rkey, post.did.clone()); + let did = post.did.clone(); + actor_id_to_did.insert(author_id, did.clone()); + post_key_to_did.insert((post.post.actor_id, post.post.rkey), did.clone()); + let uri_with_keys = (post.at_uri.clone(), post.post.actor_id, post.post.rkey, did); (author_id, uri_with_keys) }) .unzip(); @@ -243,9 +245,10 @@ } result }; + let reply_actor_cache_ref = &reply_actor_cache; let threadgates_future = async move { let start = std::time::Instant::now(); - let result = self.hydrate_threadgates(threadgates_to_hydrate, &post_key_to_did).await; + let result = self.hydrate_threadgates(threadgates_to_hydrate, &post_key_to_did, reply_actor_cache_ref).await; let elapsed = start.elapsed().as_secs_f64() * 1000.0; tracing::info!(" → Threadgates: {:.1} ms ({} gates)", elapsed, threadgate_count); if elapsed > 10.0 { @@ -281,7 +284,7 @@ tracing::warn!("Slow hydration (authors/labels/viewer/threadgates/embeds): {:.1} ms", hydrate_time); } - posts_with_stats + let results = posts_with_stats .into_iter() .filter_map(|(uri, (post, threadgate, _stats))| { let author = authors.get(&post.did)?.clone(); @@ -300,17 +303,19 @@ (post, author, labels, embed, threadgate, viewer, stats), )) }) - .collect() + .collect(); + + (results, reply_actor_cache) } pub async fn hydrate_posts(&self, posts: Vec) -> HashMap { - // For now, continue using the original build_postview which will fall back to blocking calls - // The optimization is in place but we'd need to refactor hydrate_posts_inner to return - // the actor cache to fully utilize it. This is a future optimization. - self.hydrate_posts_inner(posts) - .await + let (posts_data, actor_cache) = self.hydrate_posts_inner(posts).await; + + posts_data .into_iter() - .map(|(uri, data)| (uri, build_postview(data, self.loaders.post_state.id_cache()))) + .map(|(uri, data)| { + (uri, build_postview_with_cache(data, self.loaders.post_state.id_cache(), &actor_cache)) + }) .collect() } -- tangled.sh