notecrumbs

a nostr opengraph server build on nostrdb and egui
git clone git://jb55.com/notecrumbs
Log | Files | Refs | README | LICENSE

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:
Msrc/main.rs | 55++++++++++++++++++++++++++++---------------------------
Msrc/render.rs | 1+
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, &note_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, &note_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, - &note_rd.note_rd, - &note_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, &note_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, &note_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, &note_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 {