diff --git a/src/config.ts b/src/config.ts index 1dd688b7..33c233b8 100644 --- a/src/config.ts +++ b/src/config.ts @@ -98,6 +98,21 @@ class Conf { } return undefined; }, + /** Database query timeout configurations. */ + timeouts: { + /** Default query timeout when another setting isn't more specific. */ + get default(): number { + return Number(Deno.env.get('DB_TIMEOUT_DEFAULT') || 3_000); + }, + /** Timeout used for queries made through the Nostr relay. */ + get relay(): number { + return Number(Deno.env.get('DB_TIMEOUT_RELAY') || 1_000); + }, + /** Timeout used for timelines such as home, notifications, hashtag, etc. */ + get timelines(): number { + return Number(Deno.env.get('DB_TIMEOUT_TIMELINES') || 10_000); + }, + }, }; /** Character limit to enforce for posts made through Mastodon API. */ static get postCharLimit(): number { diff --git a/src/controllers/api/accounts.ts b/src/controllers/api/accounts.ts index ad4802ca..460aa6dd 100644 --- a/src/controllers/api/accounts.ts +++ b/src/controllers/api/accounts.ts @@ -209,7 +209,7 @@ const accountStatusesController: AppController = async (c) => { filter['#t'] = [tagged]; } - const opts = { signal, limit, timeout: 10_000 }; + const opts = { signal, limit, timeout: Conf.db.timeouts.timelines }; const events = await store.query([filter], opts) .then((events) => hydrateEvents({ events, store, signal })) diff --git a/src/controllers/api/notifications.ts b/src/controllers/api/notifications.ts index fab7c816..6f6036f2 100644 --- a/src/controllers/api/notifications.ts +++ b/src/controllers/api/notifications.ts @@ -78,7 +78,7 @@ async function renderNotifications( const store = c.get('store'); const pubkey = await c.get('signer')?.getPublicKey()!; const { signal } = c.req.raw; - const opts = { signal, limit: params.limit, timeout: 15_000 }; + const opts = { signal, limit: params.limit, timeout: Conf.db.timeouts.timelines }; const events = await store .query(filters, opts) diff --git a/src/controllers/api/timelines.ts b/src/controllers/api/timelines.ts index 62f1cd2f..848ae63f 100644 --- a/src/controllers/api/timelines.ts +++ b/src/controllers/api/timelines.ts @@ -60,7 +60,7 @@ const suggestedTimelineController: AppController = async (c) => { async function renderStatuses(c: AppContext, filters: NostrFilter[]) { const { signal } = c.req.raw; const store = c.get('store'); - const opts = { signal, timeout: 10_000 }; + const opts = { signal, timeout: Conf.db.timeouts.timelines }; const events = await store .query(filters, opts) diff --git a/src/controllers/nostr/relay.ts b/src/controllers/nostr/relay.ts index f124360e..02d2995e 100644 --- a/src/controllers/nostr/relay.ts +++ b/src/controllers/nostr/relay.ts @@ -10,6 +10,7 @@ import { } from '@nostrify/nostrify'; import { AppController } from '@/app.ts'; +import { Conf } from '@/config.ts'; import { relayInfoController } from '@/controllers/nostr/relay-info.ts'; import { relayConnectionsGauge, relayEventCounter, relayMessageCounter } from '@/metrics.ts'; import * as pipeline from '@/pipeline.ts'; @@ -95,7 +96,7 @@ function connectStream(socket: WebSocket, ip: string | undefined) { const pubsub = await Storages.pubsub(); try { - for (const event of await store.query(filters, { limit: FILTER_LIMIT, timeout: 1000 })) { + for (const event of await store.query(filters, { limit: FILTER_LIMIT, timeout: Conf.db.timeouts.relay })) { send(['EVENT', subId, event]); } } catch (e) { @@ -150,7 +151,7 @@ function connectStream(socket: WebSocket, ip: string | undefined) { /** Handle COUNT. Return the number of events matching the filters. */ async function handleCount([_, subId, ...filters]: NostrClientCOUNT): Promise { const store = await Storages.db(); - const { count } = await store.count(filters, { timeout: 100 }); + const { count } = await store.count(filters, { timeout: Conf.db.timeouts.relay }); send(['COUNT', subId, { count, approximate: false }]); } diff --git a/src/storages/EventsDB.ts b/src/storages/EventsDB.ts index d66a65b7..abf076c7 100644 --- a/src/storages/EventsDB.ts +++ b/src/storages/EventsDB.ts @@ -64,7 +64,7 @@ class EventsDB implements NStore { await this.deleteEventsAdmin(event); try { - await this.store.event(event, { ...opts, timeout: opts.timeout ?? 1000 }); + await this.store.event(event, { ...opts, timeout: opts.timeout ?? Conf.db.timeouts.default }); } catch (e) { if (e.message === 'Cannot add a deleted event') { throw new RelayError('blocked', 'event deleted by user'); @@ -164,7 +164,7 @@ class EventsDB implements NStore { this.console.debug('REQ', JSON.stringify(filters)); - return this.store.query(filters, { ...opts, timeout: opts.timeout ?? 1000 }); + return this.store.query(filters, { ...opts, timeout: opts.timeout ?? Conf.db.timeouts.default }); } /** Delete events based on filters from the database. */ @@ -172,7 +172,7 @@ class EventsDB implements NStore { if (!filters.length) return Promise.resolve(); this.console.debug('DELETE', JSON.stringify(filters)); - return this.store.remove(filters, { ...opts, timeout: opts.timeout ?? 3000 }); + return this.store.remove(filters, { ...opts, timeout: opts.timeout ?? Conf.db.timeouts.default }); } /** Get number of events that would be returned by filters. */ @@ -185,7 +185,7 @@ class EventsDB implements NStore { this.console.debug('COUNT', JSON.stringify(filters)); - return this.store.count(filters, { ...opts, timeout: opts.timeout ?? 500 }); + return this.store.count(filters, { ...opts, timeout: opts.timeout ?? Conf.db.timeouts.default }); } /** Return only the tags that should be indexed. */