From 744c1c68c8ca44fddc34bb0fc93bfc89259c3c6e Mon Sep 17 00:00:00 2001 From: Silberengel Date: Mon, 16 Mar 2026 10:26:38 +0100 Subject: [PATCH] make relay service more efficient --- Dockerfile | 6 +- src/services/client.service.ts | 143 +++++++-------------------------- 2 files changed, 32 insertions(+), 117 deletions(-) diff --git a/Dockerfile b/Dockerfile index 3bc48e37..2fac28a5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -34,8 +34,10 @@ RUN printf "server {\n\ }\n\ \n\ location / {\n\ - # For scrapers, serve index.html (they'll see static meta tags)\n\ - # Note: To get dynamic meta tags, you need SSR or a meta tag service\n\ + # For scrapers, always serve index.html so they see static og/twitter meta tags\n\ + if (\$is_scraper = 1) {\n\ + rewrite ^ /index.html last;\n\ + }\n\ try_files \$uri \$uri/ /index.html;\n\ }\n\ \n\ diff --git a/src/services/client.service.ts b/src/services/client.service.ts index 54e5e595..104ce6f9 100644 --- a/src/services/client.service.ts +++ b/src/services/client.service.ts @@ -65,10 +65,6 @@ class ClientService extends EventTarget { tokenize: 'forward' }) - /** Min delay between starting subscriptions to the same relay to avoid hammering one relay with many REQs */ - private static readonly PER_RELAY_SUB_STAGGER_MS = 80 - private lastSubStartByRelay = new Map() - constructor() { super() this.pool = new SimplePool() @@ -650,110 +646,35 @@ class ClientService extends EventTarget { // eslint-disable-next-line @typescript-eslint/no-this-alias const that = this const _knownIds = new Set() - let startedCount = 0 - let eosedCount = 0 - let eosed = false - let closedCount = 0 - const closeReasons: string[] = [] - const subPromises: Promise<{ close: () => void }>[] = [] - const staggerMs = ClientService.PER_RELAY_SUB_STAGGER_MS - relays.forEach((url) => { - let hasAuthed = false - - subPromises.push(startSub()) - - async function startSub() { - const relayKey = normalizeUrl(url) || url - const now = Date.now() - const last = that.lastSubStartByRelay.get(relayKey) ?? 0 - const wait = staggerMs - (now - last) - if (wait > 0) { - await new Promise((r) => setTimeout(r, wait)) - } - that.lastSubStartByRelay.set(relayKey, Date.now()) - startedCount++ - const relay = await that.pool.ensureRelay(url, { connectionTimeout: 5000 }).catch(() => { - return undefined - }) - // cannot connect to relay - if (!relay) { - if (!eosed) { - eosedCount++ - eosed = eosedCount >= startedCount - oneose?.(eosed) - } - return { - close: () => {} - } - } - return relay.subscribe(filters, { - receivedEvent: (relay, id) => { - that.trackEventSeenOn(id, relay) - }, - alreadyHaveEvent: (id: string) => { - const have = _knownIds.has(id) - if (have) { - return true - } - _knownIds.add(id) - return false - }, - onevent: (evt: NEvent) => { - onevent?.(evt) - }, - oneose: () => { - // make sure eosed is not called multiple times - if (eosed) return - - eosedCount++ - eosed = eosedCount >= startedCount - oneose?.(eosed) - }, - onclose: (reason: string) => { - // auth-required - if (reason.startsWith('auth-required') && !hasAuthed) { - // already logged in - if (that.signer) { - relay - .auth(async (authEvt: EventTemplate) => { - const evt = await that.signer!.signEvent(authEvt) - if (!evt) { - throw new Error('sign event failed') - } - return evt as VerifiedEvent - }) - .then(() => { - hasAuthed = true - if (!eosed) { - subPromises.push(startSub()) - } - }) - .catch(() => { - // ignore - }) - return - } - - // open login dialog - if (startLogin) { - startLogin() - return - } - } + // One request per (relay, filter) so pool groups by relay and sends one REQ per relay with all filters + const requests = relays.flatMap((url) => + filters.map((f) => ({ url: normalizeUrl(url) || url, filter: f })) + ) - // close the subscription - closedCount++ - closeReasons.push(reason) - onclose?.(url, reason) - if (closedCount >= startedCount) { - onAllClose?.(closeReasons) - } - return - }, - eoseTimeout: 10_000 // 10s - }) - } + const poolCloser = this.pool.subscribeMap(requests, { + onevent: (evt: NEvent) => onevent?.(evt), + oneose: () => oneose?.(true), + onclose: (reasons: string[]) => { + relays.forEach((url, i) => onclose?.(url, reasons[i] ?? '')) + onAllClose?.(reasons) + }, + onauth: async (authEvt: EventTemplate) => { + if (that.signer) { + const evt = await that.signer.signEvent(authEvt) + if (!evt) throw new Error('sign event failed') + return evt as VerifiedEvent + } + startLogin?.() + throw new Error('Login required') + }, + alreadyHaveEvent: (id: string) => { + const have = _knownIds.has(id) + if (have) return true + _knownIds.add(id) + return false + }, + eoseTimeout: 10_000 }) const handleNewEventFromInternal = (data: Event) => { @@ -774,15 +695,7 @@ class ClientService extends EventTarget { return { close: () => { this.removeEventListener('newEvent', handleNewEventFromInternal) - subPromises.forEach((subPromise) => { - subPromise - .then((sub) => { - sub.close() - }) - .catch(() => { - // Silent fail - }) - }) + poolCloser.close() } } }