search: fix abort signals

This commit is contained in:
Alex Gleason 2024-01-04 02:09:23 -06:00
parent 412f71599a
commit 89af83c660
No known key found for this signature in database
GPG Key ID: 7211D1F99744FBB7
3 changed files with 21 additions and 9 deletions

View File

@ -30,9 +30,11 @@ const searchController: AppController = async (c) => {
return c.json({ error: 'Bad request', schema: result.error }, 422);
}
const signal = AbortSignal.timeout(1000);
const [event, events] = await Promise.all([
lookupEvent(result.data),
searchEvents(result.data),
lookupEvent(result.data, signal),
searchEvents(result.data, signal),
]);
if (event) {
@ -62,7 +64,7 @@ const searchController: AppController = async (c) => {
};
/** Get events for the search params. */
function searchEvents({ q, type, limit, account_id }: SearchQuery): Promise<Event[]> {
function searchEvents({ q, type, limit, account_id }: SearchQuery, signal: AbortSignal): Promise<Event[]> {
if (type === 'hashtags') return Promise.resolve([]);
const filter: DittoFilter = {
@ -76,7 +78,7 @@ function searchEvents({ q, type, limit, account_id }: SearchQuery): Promise<Even
filter.authors = [account_id];
}
return searchStore.getEvents([filter]);
return searchStore.getEvents([filter], { signal });
}
/** Get event kinds to search from `type` query param. */
@ -92,7 +94,7 @@ function typeToKinds(type: SearchQuery['type']): number[] {
}
/** Resolve a searched value into an event, if applicable. */
async function lookupEvent(query: SearchQuery, signal = AbortSignal.timeout(1000)): Promise<Event | undefined> {
async function lookupEvent(query: SearchQuery, signal: AbortSignal): Promise<Event | undefined> {
const filters = await getLookupFilters(query);
const [event] = await searchStore.getEvents(filters, { limit: 1, signal });
return event;

View File

@ -5,15 +5,16 @@ interface HydrateEventOpts<K extends number> {
events: DittoEvent<K>[];
filters: DittoFilter<K>[];
storage: EventStore;
signal?: AbortSignal;
}
/** Hydrate event relationships using the provided storage. */
async function hydrateEvents<K extends number>(opts: HydrateEventOpts<K>): Promise<DittoEvent<K>[]> {
const { events, filters, storage } = opts;
const { events, filters, storage, signal } = opts;
if (filters.some((filter) => filter.relations?.includes('author'))) {
const pubkeys = new Set([...events].map((event) => event.pubkey));
const authors = await storage.getEvents([{ kinds: [0], authors: [...pubkeys] }]);
const authors = await storage.getEvents([{ kinds: [0], authors: [...pubkeys] }], { signal });
for (const event of events) {
event.author = authors.find((author) => author.pubkey === event.pubkey);

View File

@ -50,14 +50,23 @@ class SearchStore implements EventStore {
this.#debug(`Searching for "${query}" at ${this.#relay.socket.url}...`);
const sub = this.#relay.req(filters, opts);
sub.eoseSignal.onabort = () => sub.close();
const close = () => {
sub.close();
opts?.signal?.removeEventListener('abort', close);
sub.eoseSignal.removeEventListener('abort', close);
};
opts?.signal?.addEventListener('abort', close, { once: true });
sub.eoseSignal.addEventListener('abort', close, { once: true });
const events = new EventSet<DittoEvent<K>>();
for await (const event of sub) {
events.add(event);
}
return hydrateEvents({ events: [...events], filters, storage: this.#hydrator });
return hydrateEvents({ events: [...events], filters, storage: this.#hydrator, signal: opts?.signal });
} else {
this.#debug(`Searching for "${query}" locally...`);
return this.#fallback.getEvents(filters, opts);