diff --git a/src/db/events.ts b/src/db/events.ts index d977368..96cf845 100644 --- a/src/db/events.ts +++ b/src/db/events.ts @@ -354,8 +354,8 @@ function buildUserSearchContent(event: Event<0>): string { /** SQLite database storage adapter for Nostr events. */ const eventsDB: EventStore = { - storeEvent, getEvents, + storeEvent, countEvents, deleteEvents, }; diff --git a/src/db/memorelay.test.ts b/src/db/memorelay.test.ts index b0125cf..27c545d 100644 --- a/src/db/memorelay.test.ts +++ b/src/db/memorelay.test.ts @@ -5,14 +5,12 @@ import event1 from '~/fixtures/events/event-1.json' assert { type: 'json' }; import { memorelay } from './memorelay.ts'; Deno.test('memorelay', async () => { - assertEquals(memorelay.hasEvent(event1), false); - assertEquals(memorelay.hasEventById(event1.id), false); + assertEquals(await memorelay.countEvents([{ ids: [event1.id] }]), 0); - memorelay.insertEvent(event1); + await memorelay.storeEvent(event1); - assertEquals(memorelay.hasEvent(event1), true); - assertEquals(memorelay.hasEventById(event1.id), true); + assertEquals(await memorelay.countEvents([{ ids: [event1.id] }]), 1); - const result = await memorelay.getFilters([{ ids: [event1.id] }]); + const result = await memorelay.getEvents([{ ids: [event1.id] }]); assertEquals(result[0], event1); }); diff --git a/src/db/memorelay.ts b/src/db/memorelay.ts index 4f6022e..57714a0 100644 --- a/src/db/memorelay.ts +++ b/src/db/memorelay.ts @@ -1,5 +1,6 @@ import { Debug, type Event, type Filter, LRUCache } from '@/deps.ts'; -import { getFilterId, type GetFiltersOpts, getMicroFilters, isMicrofilter } from '@/filter.ts'; +import { getFilterId, getMicroFilters, isMicrofilter } from '@/filter.ts'; +import { type EventStore, type GetEventsOpts } from '@/store.ts'; const debug = Debug('ditto:memorelay'); @@ -10,7 +11,7 @@ const events = new LRUCache({ }); /** Get events from memory. */ -function getFilters(filters: Filter[], opts: GetFiltersOpts = {}): Promise[]> { +function getEvents(filters: Filter[], opts: GetEventsOpts = {}): Promise[]> { if (opts.signal?.aborted) return Promise.resolve([]); if (!filters.length) return Promise.resolve([]); debug('REQ', JSON.stringify(filters)); @@ -30,7 +31,7 @@ function getFilters(filters: Filter[], opts: GetFiltersOpts } /** Insert an event into memory. */ -function insertEvent(event: Event): void { +function storeEvent(event: Event): Promise { for (const microfilter of getMicroFilters(event)) { const filterId = getFilterId(microfilter); const existing = events.get(filterId); @@ -38,32 +39,31 @@ function insertEvent(event: Event): void { events.set(filterId, event); } } + return Promise.resolve(); } -/** Check if an event is in memory. */ -function hasEvent(event: Event): boolean { - for (const microfilter of getMicroFilters(event)) { - const filterId = getFilterId(microfilter); - const existing = events.get(filterId); - if (existing) { - return true; +/** Count events in memory for the filters. */ +async function countEvents(filters: Filter[]): Promise { + const events = await getEvents(filters); + return events.length; +} + +/** Delete events from memory. */ +function deleteEvents(filters: Filter[]): Promise { + for (const filter of filters) { + if (isMicrofilter(filter)) { + events.delete(getFilterId(filter)); } } - return false; -} - -/** Check if an event is in memory by ID. */ -function hasEventById(eventId: string): boolean { - const filterId = getFilterId({ ids: [eventId] }); - return events.has(filterId); + return Promise.resolve(); } /** In-memory data store for events using microfilters. */ -const memorelay = { - getFilters, - insertEvent, - hasEvent, - hasEventById, +const memorelay: EventStore = { + getEvents, + storeEvent, + countEvents, + deleteEvents, }; export { memorelay }; diff --git a/src/filter.ts b/src/filter.ts index 926e360..430a8d3 100644 --- a/src/filter.ts +++ b/src/filter.ts @@ -21,16 +21,6 @@ type AuthorMicrofilter = { kinds: [0]; authors: [Event['pubkey']] }; /** Filter to get one specific event. */ type MicroFilter = IdMicrofilter | AuthorMicrofilter; -/** Additional options to apply to the whole subscription. */ -interface GetFiltersOpts { - /** Signal to abort the request. */ - signal?: AbortSignal; - /** Event limit for the whole subscription. */ - limit?: number; - /** Relays to use, if applicable. */ - relays?: WebSocket['url'][]; -} - function matchDittoFilter(filter: DittoFilter, event: Event, data: EventData): boolean { if (filter.local && !(data.user || event.pubkey === Conf.pubkey)) { return false; @@ -97,7 +87,6 @@ export { type DittoFilter, eventToMicroFilter, getFilterId, - type GetFiltersOpts, getMicroFilters, type IdMicrofilter, isMicrofilter, diff --git a/src/pipeline.ts b/src/pipeline.ts index 76984fd..f4e0abe 100644 --- a/src/pipeline.ts +++ b/src/pipeline.ts @@ -26,7 +26,7 @@ const debug = Debug('ditto:pipeline'); async function handleEvent(event: Event): Promise { if (!(await verifySignatureWorker(event))) return; const wanted = reqmeister.isWanted(event); - if (encounterEvent(event)) return; + if (await encounterEvent(event)) return; debug(`Event<${event.kind}> ${event.id}`); const data = await getEventData(event); @@ -43,9 +43,9 @@ async function handleEvent(event: Event): Promise { } /** Encounter the event, and return whether it has already been encountered. */ -function encounterEvent(event: Event): boolean { - const preexisting = memorelay.hasEvent(event); - memorelay.insertEvent(event); +async function encounterEvent(event: Event): Promise { + const preexisting = (await memorelay.countEvents([{ ids: [event.id] }])) > 0; + memorelay.storeEvent(event); reqmeister.encounter(event); return preexisting; } @@ -142,7 +142,7 @@ function fetchRelatedEvents(event: Event, data: EventData) { reqmeister.req({ kinds: [0], authors: [event.pubkey] }).catch(() => {}); } for (const [name, id, relay] of event.tags) { - if (name === 'e' && !memorelay.hasEventById(id)) { + if (name === 'e' && !memorelay.countEvents([{ ids: [id] }])) { reqmeister.req({ ids: [id] }, { relays: [relay] }).catch(() => {}); } } diff --git a/src/queries.ts b/src/queries.ts index 3049144..92500d3 100644 --- a/src/queries.ts +++ b/src/queries.ts @@ -22,7 +22,7 @@ const getEvent = async ( const { kind, relations, signal = AbortSignal.timeout(1000) } = opts; const microfilter: IdMicrofilter = { ids: [id] }; - const [memoryEvent] = await memorelay.getFilters([microfilter], opts) as DittoEvent[]; + const [memoryEvent] = await memorelay.getEvents([microfilter], opts) as DittoEvent[]; if (memoryEvent && !relations) { return memoryEvent; @@ -39,14 +39,14 @@ const getEvent = async ( // TODO: make this DRY-er. if (dbEvent && !dbEvent.author) { - const [author] = await memorelay.getFilters([{ kinds: [0], authors: [dbEvent.pubkey] }], opts); + const [author] = await memorelay.getEvents([{ kinds: [0], authors: [dbEvent.pubkey] }], opts); dbEvent.author = author; } if (dbEvent) return dbEvent; if (memoryEvent && !memoryEvent.author) { - const [author] = await memorelay.getFilters([{ kinds: [0], authors: [memoryEvent.pubkey] }], opts); + const [author] = await memorelay.getEvents([{ kinds: [0], authors: [memoryEvent.pubkey] }], opts); memoryEvent.author = author; } @@ -60,7 +60,7 @@ const getAuthor = async (pubkey: string, opts: GetEventOpts<0> = {}): Promise