From e2ffbee468b2a55d6b85417d03ecf81cb1801be2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Fournier?= Date: Wed, 27 Sep 2023 15:38:00 +0200 Subject: [PATCH] chore: limit the amount of unannounced events we announce (#4845) ## About the changes When the events table is large we might be doing a full table scan searching for unannounced events. We spotted it due to a performance alert and confirmed in AWS performance insights ![image](https://github.com/Unleash/unleash/assets/455064/8e815fa3-7a1b-4453-881a-98a148eae119) The proposal is to limit this operation to 500 events (rule of thumb) per round https://github.com/Unleash/unleash/blob/f82ae354ebe6d3b0a3ddf15a051d6c8ed995c10e/src/lib/services/index.ts#L141-L147 and also ignore the events older than a day (because it seems reasonable) ## Discussion points **Idea**: split the `events` table into `recent_events` and `historical_events`. Recent can be anything from a day/week/month. This would help with recurrent queries that rely on recent data from the event's table such as optimal 304 calculation or event this scheduled task that sends unannounced events. --- src/lib/db/event-store.test.ts | 37 ++++++++++++++++++++++++++++++++++ src/lib/db/event-store.ts | 7 +++++-- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/src/lib/db/event-store.test.ts b/src/lib/db/event-store.test.ts index 72891ea9b9..9f6e42d127 100644 --- a/src/lib/db/event-store.test.ts +++ b/src/lib/db/event-store.test.ts @@ -1,6 +1,8 @@ import knex from 'knex'; import EventStore from './event-store'; import getLogger from '../../test/fixtures/no-logger'; +import { subHours, formatRFC3339 } from 'date-fns'; +import dbInit from '../../test/e2e/helpers/database-init'; beforeAll(() => { getLogger.setMuteError(true); @@ -28,3 +30,38 @@ test('Trying to get events by name if db fails should yield empty list', async ( expect(events).toBeTruthy(); expect(events.length).toBe(0); }); + +test.each([ + { + createdAt: formatRFC3339(subHours(new Date(), 1)), + expectedCount: 1, + }, + { + createdAt: formatRFC3339(subHours(new Date(), 23)), + expectedCount: 1, + }, + { + createdAt: formatRFC3339(subHours(new Date(), 25)), + expectedCount: 0, + }, +])( + 'Find unnanounced events is capped to last 24hs', + async ({ createdAt, expectedCount }) => { + const db = await dbInit('events_test', getLogger); + const type = 'application-created' as const; + const insertQuery = db.rawDatabase('events'); + await insertQuery + .insert({ + type, + created_at: createdAt, + created_by: 'a test', + data: { name: 'test', createdAt }, + }) + .returning(['id']); + + const store = new EventStore(db.rawDatabase, getLogger); + const events = await store.setUnannouncedToAnnounced(); + expect(events).toBeTruthy(); + expect(events.length).toBe(expectedCount); + }, +); diff --git a/src/lib/db/event-store.ts b/src/lib/db/event-store.ts index 767f3a519d..bafe604a4b 100644 --- a/src/lib/db/event-store.ts +++ b/src/lib/db/event-store.ts @@ -12,6 +12,7 @@ import { sharedEventEmitter } from '../util/anyEventEmitter'; import { Db } from './db'; import { Knex } from 'knex'; import EventEmitter from 'events'; +import { subDays } from 'date-fns'; const EVENT_COLUMNS = [ 'id', @@ -407,12 +408,14 @@ class EventStore implements IEventStore { return this.eventEmitter.off(eventName, listener); } - private async setUnannouncedToAnnounced(): Promise { + async setUnannouncedToAnnounced(): Promise { const rows = await this.db(TABLE) .update({ announced: true }) .where('announced', false) .whereNotNull('announced') - .returning(EVENT_COLUMNS); + .where('created_at', '>', subDays(Date.now(), 1)) + .returning(EVENT_COLUMNS) + .limit(500); return rows.map(this.rowToEvent); }