From 2c4d39425422bfbcf0cec1e46a2c76754cf027c9 Mon Sep 17 00:00:00 2001 From: Tomasz Lesniakiewicz Date: Tue, 7 Jul 2026 15:46:15 +0200 Subject: [PATCH 1/3] perf: optimize getAll with json aggregation --- lib/storage/providers/SQLiteProvider.ts | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/lib/storage/providers/SQLiteProvider.ts b/lib/storage/providers/SQLiteProvider.ts index 56fadc588..0376a9348 100644 --- a/lib/storage/providers/SQLiteProvider.ts +++ b/lib/storage/providers/SQLiteProvider.ts @@ -201,11 +201,19 @@ const provider: StorageProvider = { throw new Error('Store is not initialized!'); } - return provider.store.executeAsync('SELECT record_key, valueJSON FROM keyvaluepairs;').then(({rows}) => { - // eslint-disable-next-line no-underscore-dangle - const result = rows?._array.map((row) => [row.record_key, JSON.parse(row.valueJSON)]); - return (result ?? []) as StorageKeyValuePair[]; - }); + // We ask SQLite to combine the whole table into one JSON string, instead of returning + // every row and parsing each one in JavaScript. This way we only run JSON.parse a single + // time. Since record_key is the primary key, the rows already come back sorted by key, + // so ordering by record_key costs almost nothing. + return provider.store + .executeAsync<{aggregated: string | null}>('SELECT json_group_array(json_array(record_key, json(valueJSON))) AS aggregated FROM keyvaluepairs ORDER BY record_key;') + .then(({rows}) => { + const aggregated = rows?.item(0)?.aggregated; + if (aggregated == null) { + return []; + } + return JSON.parse(aggregated) as StorageKeyValuePair[]; + }); }, removeItem(key) { if (!provider.store) { From cbf2c70695b73cf1ef3a024ded4357919db440a7 Mon Sep 17 00:00:00 2001 From: Tomasz Lesniakiewicz Date: Tue, 7 Jul 2026 15:46:25 +0200 Subject: [PATCH 2/3] tests: add unit tests --- .../storage/providers/SQLiteProviderTest.ts | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/unit/storage/providers/SQLiteProviderTest.ts b/tests/unit/storage/providers/SQLiteProviderTest.ts index bc0cac00a..acc2e1d63 100644 --- a/tests/unit/storage/providers/SQLiteProviderTest.ts +++ b/tests/unit/storage/providers/SQLiteProviderTest.ts @@ -290,6 +290,23 @@ describe('SQLiteProvider', () => { }); }); + describe('getAll', () => { + it('should return every stored key-value pair with correct values', async () => { + await SQLiteProvider.multiSet(testEntries); + + const out = await SQLiteProvider.getAll(); + const sortedActual = [...out].sort((a, b) => a[0].localeCompare(b[0])); + const sortedExpected = [...testEntries].sort((a, b) => a[0].localeCompare(b[0])); + + expect(out).toHaveLength(5); + expect(sortedActual).toEqual(sortedExpected); + }); + + it('should return an empty array when the store is empty', async () => { + expect(await SQLiteProvider.getAll()).toEqual([]); + }); + }); + describe('removeItem', () => { it('should remove the key from the store', async () => { await SQLiteProvider.multiSet(testEntries); From 492160a7e981a90ab458978dce5d1624938cb16c Mon Sep 17 00:00:00 2001 From: Tomasz Lesniakiewicz Date: Tue, 7 Jul 2026 19:18:14 +0200 Subject: [PATCH 3/3] adjust order by --- lib/storage/providers/SQLiteProvider.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/storage/providers/SQLiteProvider.ts b/lib/storage/providers/SQLiteProvider.ts index 0376a9348..ff1fbf4d6 100644 --- a/lib/storage/providers/SQLiteProvider.ts +++ b/lib/storage/providers/SQLiteProvider.ts @@ -201,12 +201,10 @@ const provider: StorageProvider = { throw new Error('Store is not initialized!'); } - // We ask SQLite to combine the whole table into one JSON string, instead of returning - // every row and parsing each one in JavaScript. This way we only run JSON.parse a single - // time. Since record_key is the primary key, the rows already come back sorted by key, - // so ordering by record_key costs almost nothing. + // Aggregate the whole table into a single JSON string in SQLite so we only run JSON.parse + // once, instead of returning every row and parsing each one individually in JavaScript. return provider.store - .executeAsync<{aggregated: string | null}>('SELECT json_group_array(json_array(record_key, json(valueJSON))) AS aggregated FROM keyvaluepairs ORDER BY record_key;') + .executeAsync<{aggregated: string | null}>('SELECT json_group_array(json_array(record_key, json(valueJSON))) AS aggregated FROM keyvaluepairs;') .then(({rows}) => { const aggregated = rows?.item(0)?.aggregated; if (aggregated == null) {