Browse Source

fix slow feeds

imwald
Silberengel 3 weeks ago
parent
commit
393eea8c05
  1. 47
      src/components/NoteList/index.tsx
  2. 7
      src/constants.ts

47
src/components/NoteList/index.tsx

@ -1,5 +1,5 @@
import NewNotesButton from '@/components/NewNotesButton' import NewNotesButton from '@/components/NewNotesButton'
import { ExtendedKind, FIRST_RELAY_RESULT_GRACE_MS, SINGLE_RELAY_KINDLESS_REQ_LIMIT } from '@/constants' import { ExtendedKind, FIRST_RELAY_RESULT_GRACE_MS, SINGLE_RELAY_KINDLESS_EOSE_TIMEOUT_MS, SINGLE_RELAY_KINDLESS_REQ_LIMIT } from '@/constants'
import { import {
collectEmbeddedEventPrefetchTargets, collectEmbeddedEventPrefetchTargets,
getReplaceableCoordinateFromEvent, getReplaceableCoordinateFromEvent,
@ -593,6 +593,8 @@ const NoteList = forwardRef(
const singleRelayKindlessFallbackAttemptedRef = useRef(false) const singleRelayKindlessFallbackAttemptedRef = useRef(false)
const onSingleRelayKindlessEmptyRef = useRef(onSingleRelayKindlessEmpty) const onSingleRelayKindlessEmptyRef = useRef(onSingleRelayKindlessEmpty)
onSingleRelayKindlessEmptyRef.current = onSingleRelayKindlessEmpty onSingleRelayKindlessEmptyRef.current = onSingleRelayKindlessEmpty
/** Timeout handle for kindless EOSE fallback; cleared when EOSE arrives or effect tears down. */
const kindlessEoseTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)
/** Dedupe {@link toast.error} when relays return nothing for a feed load. */ /** Dedupe {@link toast.error} when relays return nothing for a feed load. */
const emptyRelayNoHitsToastKeyRef = useRef('') const emptyRelayNoHitsToastKeyRef = useRef('')
/** Per-relay outcomes for the current subscribe wave (merged shards); drives empty-feed toast detail. */ /** Per-relay outcomes for the current subscribe wave (merged shards); drives empty-feed toast detail. */
@ -1787,6 +1789,40 @@ const NoteList = forwardRef(
return undefined return undefined
} }
// Kindless single-relay mode: fall back to explicit kinds if EOSE is too slow.
// Relays that can't efficiently handle a filter with no kinds clause may hang for tens
// of seconds; the timeout fires the same fallback as the empty-EOSE path so the user
// sees content without waiting indefinitely.
if (
allowKindlessRelayExploreRef.current &&
useFilterAsIsRef.current &&
mappedSubRequests.length === 1 &&
mappedSubRequests[0] &&
mappedSubRequests[0].urls.length === 1 &&
!singleRelayKindlessFallbackAttemptedRef.current &&
onSingleRelayKindlessEmptyRef.current
) {
if (kindlessEoseTimeoutRef.current) clearTimeout(kindlessEoseTimeoutRef.current)
kindlessEoseTimeoutRef.current = setTimeout(() => {
kindlessEoseTimeoutRef.current = null
if (!effectActive) return
if (singleRelayKindlessFallbackAttemptedRef.current) return
const reqs = subRequestsRef.current
const f0 = reqs[0]
if (
reqs.length === 1 &&
f0 &&
f0.urls.length === 1 &&
allowKindlessRelayExploreRef.current &&
useFilterAsIsRef.current &&
(!f0.filter.kinds || (f0.filter.kinds as unknown[]).length === 0)
) {
singleRelayKindlessFallbackAttemptedRef.current = true
onSingleRelayKindlessEmptyRef.current?.()
}
}, SINGLE_RELAY_KINDLESS_EOSE_TIMEOUT_MS)
}
timelineSubscribePromise = client.subscribeTimeline( timelineSubscribePromise = client.subscribeTimeline(
mappedSubRequests as Array<{ urls: string[]; filter: TSubRequestFilter }>, mappedSubRequests as Array<{ urls: string[]; filter: TSubRequestFilter }>,
{ {
@ -1795,6 +1831,11 @@ const NoteList = forwardRef(
if (batch.length > 0) { if (batch.length > 0) {
feedRelayReturnedAnyEventRef.current = true feedRelayReturnedAnyEventRef.current = true
} }
// EOSE arrived — cancel the kindless timeout so the fallback doesn't fire afterwards.
if (eosed && kindlessEoseTimeoutRef.current) {
clearTimeout(kindlessEoseTimeoutRef.current)
kindlessEoseTimeoutRef.current = null
}
const narrowed = narrowLiveBatch(batch) const narrowed = narrowLiveBatch(batch)
const paintDoneBefore = feedPaintLiveRelayDoneRef.current const paintDoneBefore = feedPaintLiveRelayDoneRef.current
if (!feedPaintLiveRelayDoneRef.current) { if (!feedPaintLiveRelayDoneRef.current) {
@ -2027,6 +2068,10 @@ const NoteList = forwardRef(
followingFeedDeltaCloserRef.current?.() followingFeedDeltaCloserRef.current?.()
followingFeedDeltaCloserRef.current = null followingFeedDeltaCloserRef.current = null
setSessionFeedSnapshot(snapshotKeyForCleanup, eventsRef.current) setSessionFeedSnapshot(snapshotKeyForCleanup, eventsRef.current)
if (kindlessEoseTimeoutRef.current) {
clearTimeout(kindlessEoseTimeoutRef.current)
kindlessEoseTimeoutRef.current = null
}
if (timelinePrefetchDebounceRef.current) { if (timelinePrefetchDebounceRef.current) {
clearTimeout(timelinePrefetchDebounceRef.current) clearTimeout(timelinePrefetchDebounceRef.current)
timelinePrefetchDebounceRef.current = null timelinePrefetchDebounceRef.current = null

7
src/constants.ts

@ -153,6 +153,13 @@ export const FEED_FIRST_RELAY_RESULT_GRACE_MIN_LIMIT = 200
*/ */
export const SINGLE_RELAY_KINDLESS_REQ_LIMIT = 500 export const SINGLE_RELAY_KINDLESS_REQ_LIMIT = 500
/**
* If a kindless single-relay REQ hasn't EOSEd within this many milliseconds, fall back to an
* explicit-kinds filter (same path as when the kindless query returns no events). Prevents
* relays that are very slow on open-ended filters from stalling the home feed indefinitely.
*/
export const SINGLE_RELAY_KINDLESS_EOSE_TIMEOUT_MS = 6000
/** /**
* Minimum time between full account network hydrates (NostrProvider: relay + replaceable fetch from relays). * Minimum time between full account network hydrates (NostrProvider: relay + replaceable fetch from relays).
* IndexedDB cache still applies on every load; this only skips redundant network merges after a recent run. * IndexedDB cache still applies on every load; this only skips redundant network merges after a recent run.

Loading…
Cancel
Save