From 5a5772de3a3b49648163a8ddf00dc9eabc748934 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sat, 31 Jan 2026 19:36:08 +0000 Subject: [PATCH] perf: Batch NIP-71 metadata fetches in NostrClient Currently, when the NostrClient processes a burst of video events (e.g., initial feed load), it triggers an individual NIP-71 metadata fetch for every video that updates the `activeMap`. This results in a stampede of network requests (N videos * M relays). This change optimizes `flushEventBuffer` to collect all videos requiring metadata updates and executes a single batched `populateNip71MetadataForVideos` call at the end of the flush cycle. This reduces the metadata fetch overhead from O(N) to O(1) per batch. Verified with a local benchmark script (reduced fetch calls from 50 to 1 for 50 events) and existing unit tests. Co-authored-by: PR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> --- js/nostr/client.js | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/js/nostr/client.js b/js/nostr/client.js index 2b7e5ae3..67cf4861 100644 --- a/js/nostr/client.js +++ b/js/nostr/client.js @@ -7650,6 +7650,7 @@ export class NostrClient { const toProcess = eventBuffer; eventBuffer = []; + const videosNeedingMetadata = []; for (const evt of toProcess) { try { @@ -7706,23 +7707,30 @@ export class NostrClient { this.activeMap.set(activeKey, video); onVideo(video); // Trigger the callback that re-renders - // Fetch NIP-71 metadata (categorization tags) in the background - this.populateNip71MetadataForVideos([video]) - .then(() => { - this.applyRootCreatedAt(video); - }) - .catch((error) => { - devLogger.warn( - "[nostr] Failed to hydrate NIP-71 metadata for live video:", - error - ); - }); + // Defer NIP-71 fetch for batching + videosNeedingMetadata.push(video); } } catch (err) { devLogger.error("[subscribeVideos] Error processing event:", err); } } + if (videosNeedingMetadata.length > 0) { + // Fetch NIP-71 metadata (categorization tags) in the background + this.populateNip71MetadataForVideos(videosNeedingMetadata) + .then(() => { + for (const video of videosNeedingMetadata) { + this.applyRootCreatedAt(video); + } + }) + .catch((error) => { + devLogger.warn( + "[nostr] Failed to hydrate NIP-71 metadata for live video batch:", + error, + ); + }); + } + // Persist processed events after each flush so reloads warm quickly. this.saveLocalData("subscribeVideos:flush"); };