Refactor memorelay as an EventStore
This commit is contained in:
parent
ccfdbfeb8d
commit
56373c4ce3
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
|
|
@ -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<string, Event>({
|
|||
});
|
||||
|
||||
/** Get events from memory. */
|
||||
function getFilters<K extends number>(filters: Filter<K>[], opts: GetFiltersOpts = {}): Promise<Event<K>[]> {
|
||||
function getEvents<K extends number>(filters: Filter<K>[], opts: GetEventsOpts = {}): Promise<Event<K>[]> {
|
||||
if (opts.signal?.aborted) return Promise.resolve([]);
|
||||
if (!filters.length) return Promise.resolve([]);
|
||||
debug('REQ', JSON.stringify(filters));
|
||||
|
@ -30,7 +31,7 @@ function getFilters<K extends number>(filters: Filter<K>[], opts: GetFiltersOpts
|
|||
}
|
||||
|
||||
/** Insert an event into memory. */
|
||||
function insertEvent(event: Event): void {
|
||||
function storeEvent(event: Event): Promise<void> {
|
||||
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;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
/** Count events in memory for the filters. */
|
||||
async function countEvents(filters: Filter[]): Promise<number> {
|
||||
const events = await getEvents(filters);
|
||||
return events.length;
|
||||
}
|
||||
|
||||
/** Check if an event is in memory by ID. */
|
||||
function hasEventById(eventId: string): boolean {
|
||||
const filterId = getFilterId({ ids: [eventId] });
|
||||
return events.has(filterId);
|
||||
/** Delete events from memory. */
|
||||
function deleteEvents(filters: Filter[]): Promise<void> {
|
||||
for (const filter of filters) {
|
||||
if (isMicrofilter(filter)) {
|
||||
events.delete(getFilterId(filter));
|
||||
}
|
||||
}
|
||||
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 };
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -26,7 +26,7 @@ const debug = Debug('ditto:pipeline');
|
|||
async function handleEvent(event: Event): Promise<void> {
|
||||
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<void> {
|
|||
}
|
||||
|
||||
/** 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<boolean> {
|
||||
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(() => {});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ const getEvent = async <K extends number = number>(
|
|||
const { kind, relations, signal = AbortSignal.timeout(1000) } = opts;
|
||||
const microfilter: IdMicrofilter = { ids: [id] };
|
||||
|
||||
const [memoryEvent] = await memorelay.getFilters([microfilter], opts) as DittoEvent<K>[];
|
||||
const [memoryEvent] = await memorelay.getEvents([microfilter], opts) as DittoEvent<K>[];
|
||||
|
||||
if (memoryEvent && !relations) {
|
||||
return memoryEvent;
|
||||
|
@ -39,14 +39,14 @@ const getEvent = async <K extends number = number>(
|
|||
// 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<Ev
|
|||
const { relations, signal = AbortSignal.timeout(1000) } = opts;
|
||||
const microfilter: AuthorMicrofilter = { kinds: [0], authors: [pubkey] };
|
||||
|
||||
const [memoryEvent] = await memorelay.getFilters([microfilter], opts);
|
||||
const [memoryEvent] = await memorelay.getEvents([microfilter], opts);
|
||||
|
||||
if (memoryEvent && !relations) {
|
||||
return memoryEvent;
|
||||
|
|
Loading…
Reference in New Issue