Browse Source

speed up performance

imwald
Silberengel 4 weeks ago
parent
commit
55b6c2071a
  1. 4
      src/components/ReplyNoteList/index.tsx
  2. 16
      src/constants.ts
  3. 5
      src/hooks/useQuoteEvents.tsx
  4. 6
      src/lib/favorites-feed-relays.ts
  5. 23
      src/lib/relay-extended-tag-req-blocks.ts
  6. 15
      src/pages/primary/SpellsPage/fauxSpellFeeds.ts
  7. 13
      src/services/client-replaceable-events.service.ts
  8. 1
      src/services/client.service.ts
  9. 11
      src/services/note-stats.service.ts

4
src/components/ReplyNoteList/index.tsx

@ -1,5 +1,4 @@
import { import {
E_TAG_FILTER_BLOCKED_RELAY_URLS,
ExtendedKind, ExtendedKind,
NOTE_STATS_OP_REFERENCE_KINDS, NOTE_STATS_OP_REFERENCE_KINDS,
NOTE_STATS_OP_REFERENCE_KINDS_WITHOUT_HIGHLIGHT NOTE_STATS_OP_REFERENCE_KINDS_WITHOUT_HIGHLIGHT
@ -1127,8 +1126,7 @@ function ReplyNoteList({
...new Set([...relayHintsFromEventTags(event), ...seenOn, ...fromBrowsingFeed]) ...new Set([...relayHintsFromEventTags(event), ...seenOn, ...fromBrowsingFeed])
] ]
const replyBlockedRelays = [ const replyBlockedRelays = [
...(blockedRelays || []), ...(blockedRelays || [])
...E_TAG_FILTER_BLOCKED_RELAY_URLS
] ]
const finalRelayUrls = await buildReplyReadRelayList( const finalRelayUrls = await buildReplyReadRelayList(
opAuthorPubkey, opAuthorPubkey,

16
src/constants.ts

@ -402,7 +402,8 @@ export const DOCUMENT_RELAY_URLS = [
/** /**
* Relays that must never receive publishes: search engines, index mirrors, and similar endpoints that only ingest * Relays that must never receive publishes: search engines, index mirrors, and similar endpoints that only ingest
* or aggregate for read. Distinct from {@link SOCIAL_KIND_BLOCKED_RELAY_URLS} (kind-coverage limits, not write policy). * or aggregate for read. Use only to strip URLs from publish / write / publish-picker paths do not prepend this
* list to generic read REQ stacks. Distinct from {@link SOCIAL_KIND_BLOCKED_RELAY_URLS} (kind-coverage limits).
*/ */
export const READ_ONLY_RELAY_URLS = [ export const READ_ONLY_RELAY_URLS = [
'wss://aggr.nostr.land', 'wss://aggr.nostr.land',
@ -440,16 +441,6 @@ export const SOCIAL_KIND_BLOCKED_RELAY_URLS = [
'wss://hist.nostr.land', 'wss://hist.nostr.land',
] ]
/**
* Relays that reject certain tag filters in REQs (e.g. `#e` on some stacks) and, on nostr.sovbit.host,
* filter keys whose tag letter is uppercase (`#E`, `#A`, `#I`, ). Skip for reply/quote/stats fetches and
* whenever filters use a capital letter after `#` in a tag key (see `relayFiltersUseCapitalLetterTagKeys` in
* `relay-extended-tag-req-blocks.ts`).
*/
export const E_TAG_FILTER_BLOCKED_RELAY_URLS = [
'wss://nostr.v0l.io',
]
// Optimized relay list for read operations (includes aggregator) // Optimized relay list for read operations (includes aggregator)
export const FAST_READ_RELAY_URLS = [ export const FAST_READ_RELAY_URLS = [
'wss://theforest.nostr1.com', 'wss://theforest.nostr1.com',
@ -464,8 +455,7 @@ export const FAST_WRITE_RELAY_URLS = [
'wss://relay.damus.io', 'wss://relay.damus.io',
'wss://relay.primal.net', 'wss://relay.primal.net',
'wss://thecitadel.nostr1.com', 'wss://thecitadel.nostr1.com',
'wss://nos.lol', 'wss://nos.lol'
'wss://freelay.sovbit.host'
] ]
/** Relays used for NIP-94 file metadata (kind 1063) / GIF discovery and publish. /** Relays used for NIP-94 file metadata (kind 1063) / GIF discovery and publish.

5
src/hooks/useQuoteEvents.tsx

@ -1,5 +1,4 @@
import { import {
E_TAG_FILTER_BLOCKED_RELAY_URLS,
ExtendedKind, ExtendedKind,
FAST_READ_RELAY_URLS, FAST_READ_RELAY_URLS,
NOTE_STATS_OP_REFERENCE_KINDS_WITHOUT_HIGHLIGHT, NOTE_STATS_OP_REFERENCE_KINDS_WITHOUT_HIGHLIGHT,
@ -81,9 +80,6 @@ export function useQuoteEvents(event: Event | null, enabled: boolean) {
const userRelays = userRelayList?.read || [] const userRelays = userRelayList?.read || []
const fromFeed = browsingRelayUrls.map((u) => normalizeAnyRelayUrl(u) || u).filter(Boolean) const fromFeed = browsingRelayUrls.map((u) => normalizeAnyRelayUrl(u) || u).filter(Boolean)
const seenOn = client.getSeenEventRelayUrls(ev.id) const seenOn = client.getSeenEventRelayUrls(ev.id)
const eTagBlockedSet = new Set(
E_TAG_FILTER_BLOCKED_RELAY_URLS.map((u) => normalizeUrl(u) || u)
)
const finalRelayUrls = Array.from( const finalRelayUrls = Array.from(
new Set([ new Set([
...fromFeed, ...fromFeed,
@ -94,7 +90,6 @@ export function useQuoteEvents(event: Event | null, enabled: boolean) {
]) ])
) )
.filter(Boolean) .filter(Boolean)
.filter((u) => !eTagBlockedSet.has(normalizeUrl(u) || u))
.filter((u) => !userBlockedRelaysNorm.has((normalizeUrl(u) || u).toLowerCase())) .filter((u) => !userBlockedRelaysNorm.has((normalizeUrl(u) || u).toLowerCase()))
const filterQeId = isReplaceableEvent(ev.kind) const filterQeId = isReplaceableEvent(ev.kind)

6
src/lib/favorites-feed-relays.ts

@ -3,7 +3,6 @@ import {
DOCUMENT_RELAY_URLS, DOCUMENT_RELAY_URLS,
FAST_READ_RELAY_URLS, FAST_READ_RELAY_URLS,
PROFILE_RELAY_URLS, PROFILE_RELAY_URLS,
READ_ONLY_RELAY_URLS,
isDocumentRelayKind, isDocumentRelayKind,
relayFilterIncludesSocialKindBlockedKind relayFilterIncludesSocialKindBlockedKind
} from '@/constants' } from '@/constants'
@ -102,7 +101,7 @@ export function buildAuthorInboxOutboxRelayUrls(
/** /**
* Profile pins + Medien: author NIP-65 tier (pass from {@link buildAuthorInboxOutboxRelayUrls}), then * Profile pins + Medien: author NIP-65 tier (pass from {@link buildAuthorInboxOutboxRelayUrls}), then
* {@link READ_ONLY_RELAY_URLS}, then {@link FAST_READ_RELAY_URLS}; dedupe, blocked-stripped, capped. * {@link FAST_READ_RELAY_URLS}; dedupe, blocked-stripped, capped.
*/ */
const PROFILE_AUGMENTED_READ_MAX_RELAYS = 16 const PROFILE_AUGMENTED_READ_MAX_RELAYS = 16
@ -112,12 +111,11 @@ export function buildProfileAugmentedReadRelayUrls(
maxRelays: number = PROFILE_AUGMENTED_READ_MAX_RELAYS, maxRelays: number = PROFILE_AUGMENTED_READ_MAX_RELAYS,
useGlobalRelayBootstrap = true useGlobalRelayBootstrap = true
): string[] { ): string[] {
const readOnlyLayer = READ_ONLY_RELAY_URLS.map((u) => normalizeUrl(u) || u).filter(Boolean)
const fastReadLayer = const fastReadLayer =
useGlobalRelayBootstrap useGlobalRelayBootstrap
? (FAST_READ_RELAY_URLS.map((u) => normalizeUrl(u) || u).filter(Boolean) as string[]) ? (FAST_READ_RELAY_URLS.map((u) => normalizeUrl(u) || u).filter(Boolean) as string[])
: [] : []
const merged = mergeRelayUrlLayers([authorRelayUrls, readOnlyLayer, fastReadLayer], blockedRelays) const merged = mergeRelayUrlLayers([authorRelayUrls, fastReadLayer], blockedRelays)
return merged.slice(0, maxRelays) return merged.slice(0, maxRelays)
} }

23
src/lib/relay-extended-tag-req-blocks.ts

@ -1,18 +1,5 @@
import { E_TAG_FILTER_BLOCKED_RELAY_URLS } from '@/constants'
import { normalizeUrl } from '@/lib/url'
import type { Filter } from 'nostr-tools' import type { Filter } from 'nostr-tools'
let blockedLowerMemo: Set<string> | null = null
function extendedTagReqBlockedLowerSet(): Set<string> {
if (!blockedLowerMemo) {
blockedLowerMemo = new Set(
E_TAG_FILTER_BLOCKED_RELAY_URLS.map((u) => (normalizeUrl(u) || u).toLowerCase()).filter(Boolean)
)
}
return blockedLowerMemo
}
/** NIP-01 tag filters are `#` + tag name; keys like `#E` / `#A` / `#I` are uppercase variants. */ /** NIP-01 tag filters are `#` + tag name; keys like `#E` / `#A` / `#I` are uppercase variants. */
const CAPITAL_LEADING_TAG_FILTER_KEY = /^#[A-Z]/ const CAPITAL_LEADING_TAG_FILTER_KEY = /^#[A-Z]/
@ -33,13 +20,9 @@ export function relayFiltersUseCapitalLetterTagKeys(filter: Filter | Filter[]):
} }
/** /**
* Relays in {@link E_TAG_FILTER_BLOCKED_RELAY_URLS} reject `#e`-style queries and, on some stacks, any tag * Legacy hook after {@link E_TAG_FILTER_BLOCKED_RELAY_URLS} was removed. Call sites that still
* filter key that uses a capital letter after `#`. Drop them before REQ so we do not spam NOTICE/rate-limit responses. * run when filters use `#E`-style keys are unchanged; no relays are stripped here anymore.
*/ */
export function relayUrlsStripExtendedTagReqBlocked(urls: string[]): string[] { export function relayUrlsStripExtendedTagReqBlocked(urls: string[]): string[] {
const blocked = extendedTagReqBlockedLowerSet() return urls
return urls.filter((u) => {
const n = normalizeUrl(u) || u.trim()
return n && !blocked.has(n.toLowerCase())
})
} }

15
src/pages/primary/SpellsPage/fauxSpellFeeds.ts

@ -14,7 +14,6 @@ import {
ExtendedKind, ExtendedKind,
FAST_READ_RELAY_URLS, FAST_READ_RELAY_URLS,
PROFILE_MEDIA_TAB_KINDS, PROFILE_MEDIA_TAB_KINDS,
READ_ONLY_RELAY_URLS
} from '@/constants' } from '@/constants'
import { RENDERABLE_NOTE_KINDS_SORTED } from '@/lib/note-renderable-kinds' import { RENDERABLE_NOTE_KINDS_SORTED } from '@/lib/note-renderable-kinds'
import { buildProfileAugmentedReadRelayUrls } from '@/lib/favorites-feed-relays' import { buildProfileAugmentedReadRelayUrls } from '@/lib/favorites-feed-relays'
@ -83,11 +82,6 @@ const INTERESTS_MAX_TOPICS = 80
*/ */
const INTERESTS_MAX_TOPIC_TAG_VALUES = INTERESTS_MAX_TOPICS * 4 const INTERESTS_MAX_TOPIC_TAG_VALUES = INTERESTS_MAX_TOPICS * 4
/**
* Put {@link READ_ONLY_RELAY_URLS} (e.g. aggr) **first**, then curated relays. Faux spells cap URL count
* ({@link FAUX_SPELL_MAX_RELAYS}); appending read-only at the end dropped mirrors whenever inbox+favorites
* filled the cap.
*/
/** /**
* {@link buildPrioritizedReadRelayUrls} merges inbox favorites FAST_READ under {@link FAUX_SPELL_MAX_RELAYS}. * {@link buildPrioritizedReadRelayUrls} merges inbox favorites FAST_READ under {@link FAUX_SPELL_MAX_RELAYS}.
* Long NIP-65 read lists can fill the cap before FAST_READ is reached, so every REQ shard was only dead/private * Long NIP-65 read lists can fill the cap before FAST_READ is reached, so every REQ shard was only dead/private
@ -147,19 +141,14 @@ export function ensureFauxSpellRelayStackTouchesFastRead(urls: string[]): string
}) })
} }
/** Dedupe curated read relays and drop user-blocked URLs (no {@link READ_ONLY_RELAY_URLS} prepend). */
export function appendCuratedReadOnlyRelays(curated: string[], blockedRelays: string[]): string[] { export function appendCuratedReadOnlyRelays(curated: string[], blockedRelays: string[]): string[] {
const blocked = new Set(blockedRelays.map((b) => normalizeAnyRelayUrl(b) || b)) const blocked = new Set(blockedRelays.map((b) => normalizeAnyRelayUrl(b) || b))
const seen = new Set<string>() const seen = new Set<string>()
const out: string[] = [] const out: string[] = []
for (const u of READ_ONLY_RELAY_URLS) {
const k = normalizeAnyRelayUrl(u) || u
if (!k || blocked.has(k) || seen.has(k)) continue
seen.add(k)
out.push(k)
}
for (const u of curated) { for (const u of curated) {
const k = normalizeAnyRelayUrl(u) || u const k = normalizeAnyRelayUrl(u) || u
if (!k || seen.has(k)) continue if (!k || blocked.has(k) || seen.has(k)) continue
seen.add(k) seen.add(k)
out.push(k) out.push(k)
} }

13
src/services/client-replaceable-events.service.ts

@ -8,7 +8,6 @@ import {
METADATA_BATCH_QUERY_GLOBAL_TIMEOUT_MS, METADATA_BATCH_QUERY_GLOBAL_TIMEOUT_MS,
PROFILE_BATCH_NETWORK_LOAD_TIMEOUT_MS, PROFILE_BATCH_NETWORK_LOAD_TIMEOUT_MS,
PROFILE_RELAY_URLS, PROFILE_RELAY_URLS,
READ_ONLY_RELAY_URLS,
RECOMMENDED_BLOSSOM_SERVERS RECOMMENDED_BLOSSOM_SERVERS
} from '@/constants' } from '@/constants'
import { kinds, nip19 } from 'nostr-tools' import { kinds, nip19 } from 'nostr-tools'
@ -558,7 +557,7 @@ export class ReplaceableEventService {
// and 100ms EOSE loses the race when several relays are down. // and 100ms EOSE loses the race when several relays are down.
relayUrls = Array.from( relayUrls = Array.from(
new Set( new Set(
[...READ_ONLY_RELAY_URLS, ...PROFILE_RELAY_URLS, ...FAST_READ_RELAY_URLS].map( [...PROFILE_RELAY_URLS, ...FAST_READ_RELAY_URLS].map(
(u) => normalizeUrl(u) || u (u) => normalizeUrl(u) || u
) )
) )
@ -567,7 +566,7 @@ export class ReplaceableEventService {
// Contacts (kind 3): aggregators + profile mirrors + fast read. // Contacts (kind 3): aggregators + profile mirrors + fast read.
relayUrls = Array.from( relayUrls = Array.from(
new Set( new Set(
[...READ_ONLY_RELAY_URLS, ...PROFILE_RELAY_URLS, ...FAST_READ_RELAY_URLS].map( [...PROFILE_RELAY_URLS, ...FAST_READ_RELAY_URLS].map(
(u) => normalizeUrl(u) || u (u) => normalizeUrl(u) || u
) )
) )
@ -576,7 +575,7 @@ export class ReplaceableEventService {
// NIP-65 (10002): aggregators + profile mirrors + fast read. // NIP-65 (10002): aggregators + profile mirrors + fast read.
relayUrls = Array.from( relayUrls = Array.from(
new Set( new Set(
[...READ_ONLY_RELAY_URLS, ...PROFILE_RELAY_URLS, ...FAST_READ_RELAY_URLS].map( [...PROFILE_RELAY_URLS, ...FAST_READ_RELAY_URLS].map(
(u) => normalizeUrl(u) || u (u) => normalizeUrl(u) || u
) )
) )
@ -585,7 +584,7 @@ export class ReplaceableEventService {
// Mute / bookmark lists: same distribution as contacts; FAST_READ + mirrors. // Mute / bookmark lists: same distribution as contacts; FAST_READ + mirrors.
relayUrls = Array.from( relayUrls = Array.from(
new Set( new Set(
[...READ_ONLY_RELAY_URLS, ...PROFILE_RELAY_URLS, ...FAST_READ_RELAY_URLS].map( [...PROFILE_RELAY_URLS, ...FAST_READ_RELAY_URLS].map(
(u) => normalizeUrl(u) || u (u) => normalizeUrl(u) || u
) )
) )
@ -594,7 +593,7 @@ export class ReplaceableEventService {
// NIP-A3 kind 10133: aggregators + profile mirrors + fast read. // NIP-A3 kind 10133: aggregators + profile mirrors + fast read.
relayUrls = Array.from( relayUrls = Array.from(
new Set( new Set(
[...READ_ONLY_RELAY_URLS, ...PROFILE_RELAY_URLS, ...FAST_READ_RELAY_URLS].map( [...PROFILE_RELAY_URLS, ...FAST_READ_RELAY_URLS].map(
(u) => normalizeUrl(u) || u (u) => normalizeUrl(u) || u
) )
) )
@ -1244,7 +1243,7 @@ export class ReplaceableEventService {
/** /**
* Fetch follow list event. * Fetch follow list event.
* When relayUrls are provided (e.g. user write + search relays), queries those directly. * When relayUrls are provided (e.g. user write + search relays), queries those directly.
* Otherwise uses the default relay set (READ_ONLY + PROFILE_FETCH + FAST_READ). * Otherwise uses the default relay set (PROFILE_RELAY_URLS + FAST_READ).
*/ */
/** Hard cap: {@link fetchReplaceableEvent} can otherwise wedge the DataLoader chain when relays never answer. */ /** Hard cap: {@link fetchReplaceableEvent} can otherwise wedge the DataLoader chain when relays never answer. */
private static readonly FETCH_FOLLOW_LIST_REPLACEABLE_TIMEOUT_MS = 14_000 private static readonly FETCH_FOLLOW_LIST_REPLACEABLE_TIMEOUT_MS = 14_000

1
src/services/client.service.ts

@ -30,7 +30,6 @@ import {
DEFAULT_FAVORITE_RELAYS, DEFAULT_FAVORITE_RELAYS,
NIP66_DISCOVERY_RELAY_URLS, NIP66_DISCOVERY_RELAY_URLS,
PROFILE_RELAY_URLS, PROFILE_RELAY_URLS,
PROFILE_RELAY_URLS,
READ_ONLY_RELAY_URLS, READ_ONLY_RELAY_URLS,
NIP42_POOL_AUTOMATIC_AUTH_RELAY_URLS, NIP42_POOL_AUTOMATIC_AUTH_RELAY_URLS,
SEARCHABLE_RELAY_URLS SEARCHABLE_RELAY_URLS

11
src/services/note-stats.service.ts

@ -1,5 +1,4 @@
import { import {
E_TAG_FILTER_BLOCKED_RELAY_URLS,
ExtendedKind, ExtendedKind,
FAST_READ_RELAY_URLS, FAST_READ_RELAY_URLS,
NOTE_STATS_OP_REFERENCE_KINDS_WITHOUT_HIGHLIGHT, NOTE_STATS_OP_REFERENCE_KINDS_WITHOUT_HIGHLIGHT,
@ -35,7 +34,7 @@ import {
getNip25ReactionTargetHexFromTags, getNip25ReactionTargetHexFromTags,
tagNameEquals tagNameEquals
} from '@/lib/tag' } from '@/lib/tag'
import { normalizeAnyRelayUrl, normalizeUrl } from '@/lib/url' import { normalizeAnyRelayUrl } from '@/lib/url'
import client, { eventService } from '@/services/client.service' import client, { eventService } from '@/services/client.service'
import { TEmoji, type TRelayList } from '@/types' import { TEmoji, type TRelayList } from '@/types'
import dayjs from 'dayjs' import dayjs from 'dayjs'
@ -589,12 +588,8 @@ class NoteStatsService {
/** /**
* Build relay list for note stats: SEARCHABLE + FAST_READ + optional user favorites + seen relays + * Build relay list for note stats: SEARCHABLE + FAST_READ + optional user favorites + seen relays +
* `e`-tag hints on the note + hints from session-cached referrers + author NIP-65 read (slice 10). * `e`-tag hints on the note + hints from session-cached referrers + author NIP-65 read (slice 10).
* Excludes E_TAG_FILTER_BLOCKED_RELAY_URLS (stats use #e filters).
*/ */
private async buildNoteStatsRelayList(event: Event, favoriteRelays?: string[] | null): Promise<string[]> { private async buildNoteStatsRelayList(event: Event, favoriteRelays?: string[] | null): Promise<string[]> {
const blocked = new Set(
E_TAG_FILTER_BLOCKED_RELAY_URLS.map((u) => (normalizeUrl(u) || u).toLowerCase()).filter(Boolean)
)
const seen = new Set<string>() const seen = new Set<string>()
const add = (url: string | undefined) => { const add = (url: string | undefined) => {
@ -602,8 +597,7 @@ class NoteStatsService {
// Must use normalizeAnyRelayUrl, not normalizeUrl: the latter converts http(s):// // Must use normalizeAnyRelayUrl, not normalizeUrl: the latter converts http(s)://
// index relay URLs into ws(s):// which then hit the WebSocket pool. // index relay URLs into ws(s):// which then hit the WebSocket pool.
const n = normalizeAnyRelayUrl(url) const n = normalizeAnyRelayUrl(url)
if (!n || blocked.has(n.toLowerCase()) || seen.has(n)) return if (n) seen.add(n)
seen.add(n)
} }
// 1. Search / discovery relay set (includes read-only index mirrors; see READ_ONLY_RELAY_URLS in constants) // 1. Search / discovery relay set (includes read-only index mirrors; see READ_ONLY_RELAY_URLS in constants)
@ -660,7 +654,6 @@ class NoteStatsService {
return feedRelayPolicyUrls([{ source: 'fallback', urls: Array.from(seen) }], { return feedRelayPolicyUrls([{ source: 'fallback', urls: Array.from(seen) }], {
operation: 'read', operation: 'read',
blockedRelays: E_TAG_FILTER_BLOCKED_RELAY_URLS,
applySocialKindBlockedFilter: false, applySocialKindBlockedFilter: false,
allowThirdPartyLocalRelays: true allowThirdPartyLocalRelays: true
}) })

Loading…
Cancel
Save