commit 38670b3972b6690ec7705f7694927727af674621
parent 1c842b717e9db76d6c0b9b98e3e7023d197b1c90
Author: William Casarin <jb55@jb55.com>
Date: Tue, 17 Feb 2026 11:08:53 -0800
perf: move secondary note fetches to background task
Unknowns, stats, and reply profile fetches were blocking the render
pipeline with up to ~7.5s of sequential relay timeouts. Move them
into a tokio::spawn fire-and-forget task so pages render instantly
with whatever is in ndb. Next load picks up the full data.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat:
2 files changed, 29 insertions(+), 27 deletions(-)
diff --git a/src/main.rs b/src/main.rs
@@ -212,39 +212,40 @@ async fn serve(
}
}
- // Collect and fetch all unknowns from the note (author, mentions, quotes, replies)
+ // Fetch secondary data (unknowns, stats, reply profiles) in the background.
+ // Render immediately with whatever is in ndb; next load will have the full data.
if let RenderData::Note(note_rd) = &render_data {
- if let Some(unknowns) = render::collect_note_unknowns(&app.ndb, ¬e_rd.note_rd) {
- tracing::debug!("fetching {} unknowns", unknowns.ids_len());
- if let Err(err) = render::fetch_unknowns(&app.relay_pool, &app.ndb, unknowns).await {
- tracing::warn!("failed to fetch unknowns: {err}");
+ let ndb = app.ndb.clone();
+ let relay_pool = app.relay_pool.clone();
+ let note_rd_bg = note_rd.note_rd.clone();
+ let source_relays = note_rd.source_relays.clone();
+ tokio::spawn(async move {
+ // Fetch unknowns (author, mentions, quotes, reply chain)
+ if let Some(unknowns) = render::collect_note_unknowns(&ndb, ¬e_rd_bg) {
+ tracing::debug!("fetching {} unknowns", unknowns.ids_len());
+ if let Err(err) = render::fetch_unknowns(&relay_pool, &ndb, unknowns).await {
+ tracing::warn!("failed to fetch unknowns: {err}");
+ }
}
- }
-
- // Fetch note stats (reactions, replies, reposts) for the note
- if let Err(err) = render::fetch_note_stats(
- &app.relay_pool,
- &app.ndb,
- ¬e_rd.note_rd,
- ¬e_rd.source_relays,
- )
- .await
- {
- tracing::warn!("failed to fetch note stats: {err}");
- }
- // Fetch profiles for reply authors (now that replies are ingested)
- if let Some(reply_unknowns) = render::collect_reply_unknowns(&app.ndb, ¬e_rd.note_rd) {
- tracing::debug!(
- "fetching {} reply author profiles",
- reply_unknowns.ids_len()
- );
+ // Fetch note stats (reactions, replies, reposts)
if let Err(err) =
- render::fetch_unknowns(&app.relay_pool, &app.ndb, reply_unknowns).await
+ render::fetch_note_stats(&relay_pool, &ndb, ¬e_rd_bg, &source_relays).await
{
- tracing::warn!("failed to fetch reply author profiles: {err}");
+ tracing::warn!("failed to fetch note stats: {err}");
}
- }
+
+ // Fetch profiles for reply authors (now that replies are ingested)
+ if let Some(reply_unknowns) = render::collect_reply_unknowns(&ndb, ¬e_rd_bg) {
+ tracing::debug!(
+ "fetching {} reply author profiles",
+ reply_unknowns.ids_len()
+ );
+ if let Err(err) = render::fetch_unknowns(&relay_pool, &ndb, reply_unknowns).await {
+ tracing::warn!("failed to fetch reply author profiles: {err}");
+ }
+ }
+ });
}
if let RenderData::Profile(profile_opt) = &render_data {
diff --git a/src/render.rs b/src/render.rs
@@ -33,6 +33,7 @@ pub const PROFILE_FEED_RECENT_LIMIT: usize = 12;
pub const PROFILE_FEED_LOOKBACK_DAYS: u64 = 30;
pub const DIRECT_REPLY_LIMIT: i32 = 50;
+#[derive(Clone)]
pub enum NoteRenderData {
Missing([u8; 32]),
Address {