From 018ee0e0f32102f51dd06b59459931e2cef5a841 Mon Sep 17 00:00:00 2001 From: Jure <44338+hoornet@users.noreply.github.com> Date: Mon, 13 Apr 2026 22:16:02 +0200 Subject: [PATCH] Fix thread OOM: hard-truncate events client-side, remove batchFetchProfileAges from thread --- src/components/thread/ThreadView.tsx | 3 +-- src/lib/nostr/notes.ts | 14 +++++++------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/components/thread/ThreadView.tsx b/src/components/thread/ThreadView.tsx index 711ee3c..cadfb30 100644 --- a/src/components/thread/ThreadView.tsx +++ b/src/components/thread/ThreadView.tsx @@ -4,7 +4,7 @@ import { useAutoResize } from "../../hooks/useAutoResize"; import { useUIStore } from "../../stores/ui"; import { useUserStore } from "../../stores/user"; import { useMuteStore } from "../../stores/mute"; -import { fetchNoteById, fetchThreadEvents, fetchAncestors, publishReply, getNDK, ensureConnected, batchFetchProfileAges } from "../../lib/nostr"; +import { fetchNoteById, fetchThreadEvents, fetchAncestors, publishReply, getNDK, ensureConnected } from "../../lib/nostr"; import { buildThreadTree, getRootEventId } from "../../lib/threadTree"; import type { ThreadNode } from "../../lib/threadTree"; import { debug } from "../../lib/debug"; @@ -106,7 +106,6 @@ export function ThreadView() { const built = buildThreadTree(root.id, allEvents); setTree(built); - batchFetchProfileAges([...new Set(allEvents.map((e) => e.pubkey))]); } catch (err) { debug.error("Failed to load thread:", err); if (!cancelled) setLoadError(`Failed to load: ${err}`); diff --git a/src/lib/nostr/notes.ts b/src/lib/nostr/notes.ts index 1f7016d..09ccde5 100644 --- a/src/lib/nostr/notes.ts +++ b/src/lib/nostr/notes.ts @@ -146,16 +146,16 @@ export async function fetchThreadEvents(rootId: string): Promise { const directEvents = await fetchWithTimeout(instance, directFilter, THREAD_TIMEOUT); const allEvents = new Map(); - for (const e of directEvents) allEvents.set(e.id, e); + // Hard-truncate: relays often ignore `limit` on #e filters, so we enforce it client-side + for (const e of [...directEvents].slice(0, THREAD_EVENT_LIMIT)) allEvents.set(e.id, e); - // Round-trip 2: replies to events in the thread — only if round 1 returned < limit - // Skip deep fetch on large threads to avoid OOM - if (allEvents.size < THREAD_EVENT_LIMIT) { - const knownIds = Array.from(allEvents.keys()).slice(0, 50); // cap #e filter size + // Round-trip 2: only attempt if round 1 was small (skip entirely on big threads) + if (allEvents.size < 50) { + const knownIds = Array.from(allEvents.keys()); if (knownIds.length > 0) { - const deepFilter: NDKFilter = { kinds: [NDKKind.Text], "#e": knownIds, limit: THREAD_EVENT_LIMIT - allEvents.size }; + const deepFilter: NDKFilter = { kinds: [NDKKind.Text], "#e": knownIds, limit: THREAD_EVENT_LIMIT }; const deepEvents = await fetchWithTimeout(instance, deepFilter, THREAD_TIMEOUT); - for (const e of deepEvents) allEvents.set(e.id, e); + for (const e of [...deepEvents].slice(0, THREAD_EVENT_LIMIT - allEvents.size)) allEvents.set(e.id, e); } }