From c5e9017d24b6bedc7a0e616be4391f7a76e6a3ab Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 4 Mar 2025 22:14:56 -0600 Subject: [PATCH 1/3] Fetch related events by ID through the RelayStore --- packages/ditto/app.ts | 2 +- .../ditto/storages/DittoRelayStore.test.ts | 23 ++++++++++- packages/ditto/storages/DittoRelayStore.ts | 41 ++++++++++++++++++- scripts/db-populate-nip05.ts | 3 +- 4 files changed, 64 insertions(+), 5 deletions(-) diff --git a/packages/ditto/app.ts b/packages/ditto/app.ts index 931b3825..af0f9cfb 100644 --- a/packages/ditto/app.ts +++ b/packages/ditto/app.ts @@ -194,7 +194,7 @@ const pgstore = new DittoPgStore({ }); const pool = new DittoPool({ conf, relay: pgstore }); -const relay = new DittoRelayStore({ db, conf, relay: pgstore }); +const relay = new DittoRelayStore({ db, conf, pool, relay: pgstore }); await seedZapSplits({ conf, relay }); diff --git a/packages/ditto/storages/DittoRelayStore.test.ts b/packages/ditto/storages/DittoRelayStore.test.ts index 407b77e5..0db69ba7 100644 --- a/packages/ditto/storages/DittoRelayStore.test.ts +++ b/packages/ditto/storages/DittoRelayStore.test.ts @@ -65,9 +65,29 @@ Deno.test('updateAuthorData sets nip05', async () => { assertEquals(row?.nip05_hostname, 'gleasonator.dev'); }); +Deno.test('fetchRelated', async () => { + await using test = setupTest(); + const { pool, store } = test; + + const post = genEvent({ kind: 1, content: 'hi' }); + const reply = genEvent({ kind: 1, content: 'wussup?', tags: [['e', post.id], ['p', post.pubkey]] }); + + await pool.event(post); + await pool.event(reply); + + await store.event(reply); + + await waitFor(async () => { + const { count } = await test.store.count([{ ids: [post.id] }]); + return count > 0; + }, 3000); +}); + function setupTest(cb?: (req: Request) => Response | Promise) { const conf = new DittoConf(Deno.env); const db = new DittoPolyPg(conf.databaseUrl); + + const pool = new MockRelay(); const relay = new MockRelay(); const mockFetch: typeof fetch = async (input, init) => { @@ -79,11 +99,12 @@ function setupTest(cb?: (req: Request) => Response | Promise) { } }; - const store = new DittoRelayStore({ conf, db, relay, fetch: mockFetch }); + const store = new DittoRelayStore({ conf, db, pool, relay, fetch: mockFetch }); return { db, conf, + pool, store, [Symbol.asyncDispose]: async () => { await store[Symbol.asyncDispose](); diff --git a/packages/ditto/storages/DittoRelayStore.ts b/packages/ditto/storages/DittoRelayStore.ts index 5ea1372a..fc29f1f6 100644 --- a/packages/ditto/storages/DittoRelayStore.ts +++ b/packages/ditto/storages/DittoRelayStore.ts @@ -28,7 +28,7 @@ import { DittoPush } from '@/DittoPush.ts'; import { DittoEvent } from '@/interfaces/DittoEvent.ts'; import { RelayError } from '@/RelayError.ts'; import { hydrateEvents } from '@/storages/hydrate.ts'; -import { eventAge, nostrNow, Time } from '@/utils.ts'; +import { eventAge, isNostrId, nostrNow, Time } from '@/utils.ts'; import { getAmount } from '@/utils/bolt11.ts'; import { errorJson } from '@/utils/log.ts'; import { purifyEvent } from '@/utils/purify.ts'; @@ -46,6 +46,7 @@ import { nip19 } from 'nostr-tools'; interface DittoRelayStoreOpts { db: DittoDB; conf: DittoConf; + pool: NRelay; relay: NRelay; fetch?: typeof fetch; } @@ -192,7 +193,12 @@ export class DittoRelayStore implements NRelay { this.prewarmLinkPreview(event, signal), this.generateSetEvents(event), ]) - .then(() => this.webPush(event)) + .then(() => + Promise.allSettled([ + this.webPush(event), + this.fetchRelated(event), + ]) + ) .catch(() => {}); } } @@ -323,6 +329,37 @@ export class DittoRelayStore implements NRelay { } } + private async fetchRelated(event: NostrEvent): Promise { + const ids = new Set(); + + for (const tag of event.tags) { + const [name, value] = tag; + + if ((name === 'e' || name === 'q') && isNostrId(value) && !this.encounters.has(value)) { + ids.add(value); + } + } + + const { db, pool } = this.opts; + + if (ids.size) { + const query = db.kysely + .selectFrom('nostr_events') + .select('id') + .where('id', 'in', [...ids]); + + for (const row of await query.execute().catch(() => [])) { + ids.delete(row.id); + } + } + + if (ids.size) { + for (const event of await pool.query([{ ids: [...ids] }])) { + await this.event(event).catch(() => {}); + } + } + } + private async prewarmLinkPreview(event: NostrEvent, signal?: AbortSignal): Promise { const { firstUrl } = parseNoteContent(stripimeta(event.content, event.tags), [], this.opts); diff --git a/scripts/db-populate-nip05.ts b/scripts/db-populate-nip05.ts index 49866579..f282b674 100644 --- a/scripts/db-populate-nip05.ts +++ b/scripts/db-populate-nip05.ts @@ -1,5 +1,6 @@ import { Semaphore } from '@core/asyncutil'; import { NostrEvent } from '@nostrify/nostrify'; +import { MockRelay } from '@nostrify/nostrify/test'; import { DittoConf } from '@ditto/conf'; import { DittoPolyPg } from '@ditto/db'; @@ -11,7 +12,7 @@ const conf = new DittoConf(Deno.env); const db = new DittoPolyPg(conf.databaseUrl); const pgstore = new DittoPgStore({ db, conf }); -const relaystore = new DittoRelayStore({ conf, db, relay: pgstore }); +const relaystore = new DittoRelayStore({ conf, db, pool: new MockRelay(), relay: pgstore }); const sem = new Semaphore(5); From 815b903e28ce6cbd4354b7999fca304af54f8200 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 4 Mar 2025 23:07:45 -0600 Subject: [PATCH 2/3] Add a timeout on pool.query --- packages/ditto/storages/DittoRelayStore.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/ditto/storages/DittoRelayStore.ts b/packages/ditto/storages/DittoRelayStore.ts index fc29f1f6..246a9c9d 100644 --- a/packages/ditto/storages/DittoRelayStore.ts +++ b/packages/ditto/storages/DittoRelayStore.ts @@ -354,7 +354,7 @@ export class DittoRelayStore implements NRelay { } if (ids.size) { - for (const event of await pool.query([{ ids: [...ids] }])) { + for (const event of await pool.query([{ ids: [...ids] }], { signal: AbortSignal.timeout(1000) })) { await this.event(event).catch(() => {}); } } From b5858dd54c36e62dbd571386be2acf937259f027 Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Tue, 4 Mar 2025 23:09:20 -0600 Subject: [PATCH 3/3] Catch pool.query --- packages/ditto/storages/DittoRelayStore.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/ditto/storages/DittoRelayStore.ts b/packages/ditto/storages/DittoRelayStore.ts index 246a9c9d..794613ce 100644 --- a/packages/ditto/storages/DittoRelayStore.ts +++ b/packages/ditto/storages/DittoRelayStore.ts @@ -354,7 +354,9 @@ export class DittoRelayStore implements NRelay { } if (ids.size) { - for (const event of await pool.query([{ ids: [...ids] }], { signal: AbortSignal.timeout(1000) })) { + const signal = AbortSignal.timeout(1000); + + for (const event of await pool.query([{ ids: [...ids] }], { signal }).catch(() => [])) { await this.event(event).catch(() => {}); } }