From b7624666d65d96e9b9db62439a164e9625883c55 Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Fri, 6 Jun 2025 02:13:53 -0400 Subject: [PATCH] implement QuantumKVCache.add and QuantumKVCache.addMany --- packages/backend/src/misc/cache.ts | 22 +++++++++ packages/backend/test/unit/misc/cache.ts | 62 ++++++++++++++++++++++++ 2 files changed, 84 insertions(+) diff --git a/packages/backend/src/misc/cache.ts b/packages/backend/src/misc/cache.ts index 22201e243f..3145550a44 100644 --- a/packages/backend/src/misc/cache.ts +++ b/packages/backend/src/misc/cache.ts @@ -565,6 +565,28 @@ export class QuantumKVCache implements Iterable<[key: string, value: T]> { } } + /** + * Adds a value to the local memory cache without notifying other process. + * Neither a Redis event nor onSet callback will be fired, as the value has not actually changed. + * This should only be used when the value is known to be current, like after fetching from the database. + */ + @bindThis + public add(key: string, value: T): void { + this.memoryCache.set(key, value); + } + + /** + * Adds multiple values to the local memory cache without notifying other process. + * Neither a Redis event nor onSet callback will be fired, as the value has not actually changed. + * This should only be used when the value is known to be current, like after fetching from the database. + */ + @bindThis + public addMany(items: Iterable<[key: string, value: T]>): void { + for (const [key, value] of items) { + this.memoryCache.set(key, value); + } + } + /** * Gets a value from the local memory cache, or returns undefined if not found. */ diff --git a/packages/backend/test/unit/misc/cache.ts b/packages/backend/test/unit/misc/cache.ts index 0b658618e6..e24f6d4dcc 100644 --- a/packages/backend/test/unit/misc/cache.ts +++ b/packages/backend/test/unit/misc/cache.ts @@ -417,6 +417,68 @@ describe(QuantumKVCache, () => { }); }); + describe('add', () => { + it('should add the item', () => { + const cache = makeCache(); + cache.add('foo', 'bar'); + expect(cache.has('foo')).toBe(true); + }); + + it('should not emit event', () => { + const cache = makeCache({ + name: 'fake', + }); + + cache.add('foo', 'bar'); + + expect(fakeInternalEventService._calls.filter(c => c[0] === 'emit')).toHaveLength(0); + }); + + it('should not call onSet', () => { + const fakeOnSet = jest.fn(() => Promise.resolve()); + const cache = makeCache({ + onSet: fakeOnSet, + }); + + cache.add('foo', 'bar'); + + expect(fakeOnSet).not.toHaveBeenCalled(); + }); + }); + + describe('addMany', () => { + it('should add all items', () => { + const cache = makeCache(); + + cache.addMany([['foo', 'bar'], ['alpha', 'omega']]); + + expect(cache.has('foo')).toBe(true); + expect(cache.has('alpha')).toBe(true); + }); + + + it('should not emit event', () => { + const cache = makeCache({ + name: 'fake', + }); + + cache.addMany([['foo', 'bar'], ['alpha', 'omega']]); + + expect(fakeInternalEventService._calls.filter(c => c[0] === 'emit')).toHaveLength(0); + }); + + it('should not call onSet', () => { + const fakeOnSet = jest.fn(() => Promise.resolve()); + const cache = makeCache({ + onSet: fakeOnSet, + }); + + cache.addMany([['foo', 'bar'], ['alpha', 'omega']]); + + expect(fakeOnSet).not.toHaveBeenCalled(); + }); + }); + describe('has', () => { it('should return false when empty', () => { const cache = makeCache();