From 5d6f2222ead017d16bbc15e0d2004cd538865b4a Mon Sep 17 00:00:00 2001 From: Adam Trotta Date: Wed, 4 Oct 2023 19:24:11 -0400 Subject: [PATCH] feat: Cache parsed SQL in Event class This reduces the number of times we need to parse the SQL, which is expensive, and improves sequence diagram load times by ~24% --- packages/models/src/event.js | 19 +++++++++++++++---- packages/models/tests/unit/event.spec.js | 15 ++++++++++++--- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/packages/models/src/event.js b/packages/models/src/event.js index fe5f02b949..96d9a27f86 100644 --- a/packages/models/src/event.js +++ b/packages/models/src/event.js @@ -19,6 +19,8 @@ function alias(obj, prop, alias) { // This class supercedes `CallTree` and `CallNode`. Events are stored in a flat // array and can also be traversed like a tree via `parent` and `children`. export default class Event { + static parsedSqlCache = {}; + static contentType(...messages) { const msg = messages.find((message) => (message?.headers || {})['Content-Type']); if (!msg) { @@ -526,10 +528,19 @@ export default class Event { let properties; if (sqlQuery) { - const sqlNormalized = abstractSqlAstJSON(sqlQuery, this.sql.database_type) - // Collapse repeated variable literals and parameter tokens (e.g. '?, ?' in an IN clause) - .split(/{"type":"variable"}(?:,{"type":"variable"})*/g) - .join(`{"type":"variable"}`); + let sqlNormalized; + const cacheKey = `${this.sql.database_type}:${sqlQuery}`; + if (!Event.parsedSqlCache[cacheKey]) { + sqlNormalized = abstractSqlAstJSON(sqlQuery, this.sql.database_type) + // Collapse repeated variable literals and parameter tokens (e.g. '?, ?' in an IN clause) + .split(/{"type":"variable"}(?:,{"type":"variable"})*/g) + .join(`{"type":"variable"}`); + + Event.parsedSqlCache[cacheKey] = sqlNormalized; + } else { + sqlNormalized = Event.parsedSqlCache[cacheKey]; + } + properties = { event_type: 'sql', sql_normalized: sqlNormalized, diff --git a/packages/models/tests/unit/event.spec.js b/packages/models/tests/unit/event.spec.js index 477504b04b..8516b33e05 100644 --- a/packages/models/tests/unit/event.spec.js +++ b/packages/models/tests/unit/event.spec.js @@ -101,12 +101,21 @@ describe('Event', () => { }); }); describe('stableProperties', () => { - it('sql_normalized', () => { + it('sql_normalized and cached', () => { + const expectedSqlNormalized = + '{"type":"statement","variant":"list","statement":[{"type":"statement","variant":"select","result":[{"type":"function","name":{"type":"identifier","variant":"function","name":"count"},"args":{"type":"identifier","variant":"star","name":"*"}}],"from":{"type":"identifier","variant":"table","name":"spree_stores"}}]}'; + verifyJSON(sqlEvent.gatherStableProperties(), { event_type: 'sql', - sql_normalized: - '{"type":"statement","variant":"list","statement":[{"type":"statement","variant":"select","result":[{"type":"function","name":{"type":"identifier","variant":"function","name":"count"},"args":{"type":"identifier","variant":"star","name":"*"}}],"from":{"type":"identifier","variant":"table","name":"spree_stores"}}]}', + sql_normalized: expectedSqlNormalized, }); + + const expectedSqlKey = `${sqlEvent.sql.database_type}:${sqlEvent.sqlQuery}`; + const expectedSqlCache = { + [expectedSqlKey]: expectedSqlNormalized, + }; + + expect(Event.parsedSqlCache).toEqual(expectedSqlCache); }); it('sql_normalized with query parameters', () => { const sql = {