Skip to content

Commit 9eae729

Browse files
authored
cacheable - fix: adding in a unique cacheId to handle wrap async conflicts for each cache instance (#974)
1 parent 8c6ec41 commit 9eae729

File tree

6 files changed

+44
-5
lines changed

6 files changed

+44
-5
lines changed

packages/cache-manager/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ const cache = createCache({ stores: [keyv] });
183183

184184
- **cacheId**?: string - Defaults to random string
185185

186-
Unique identifier for the cache instance.
186+
Unique identifier for the cache instance. This is primarily used to not have conflicts when using `wrap` with multiple cache instances.
187187

188188
# Methods
189189
## set

packages/cache-manager/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ export const createCache = (options?: CreateCacheOptions): Cache => {
253253
fnc: () => T | Promise<T>,
254254
ttl?: number | ((value: T) => number),
255255
refreshThreshold?: number,
256-
): Promise<T> => coalesceAsync(`${_cacheId}__${key}`, async () => {
256+
): Promise<T> => coalesceAsync(`${_cacheId}::${key}`, async () => {
257257
let value: T | undefined;
258258
let i = 0;
259259
let remainingTtl: number | undefined;

packages/cache-manager/test/wrap.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ describe('wrap', () => {
8989
const getValueA = vi.fn(() => 'A');
9090
const getValueB = vi.fn(() => 'B');
9191
const anotherCache = createCache({stores: [new Keyv()]});
92-
expect(await cache.wrap(data.key, async () => anotherCache.wrap(data.key, getValueB).then((v) => v + getValueA()))).toEqual('BA');
92+
expect(await cache.wrap(data.key, async () => anotherCache.wrap(data.key, getValueB).then(v => v + getValueA()))).toEqual('BA');
9393
expect(getValueA).toHaveBeenCalledOnce();
9494
expect(getValueB).toHaveBeenCalledOnce();
9595
});
@@ -110,7 +110,7 @@ describe('wrap', () => {
110110
const getValueA = vi.fn(() => 'A');
111111
const getValueB = vi.fn(() => 'B');
112112
const anotherCache = createCache({stores: [new Keyv()]});
113-
expect(await cache.wrap(data.key, async () => anotherCache.wrap(data.key, getValueB).then((v) => v + getValueA()))).toEqual('BA');
113+
expect(await cache.wrap(data.key, async () => anotherCache.wrap(data.key, getValueB).then(v => v + getValueA()))).toEqual('BA');
114114
expect(getValueA).toHaveBeenCalledOnce();
115115
expect(getValueB).toHaveBeenCalledOnce();
116116
});

packages/cacheable/src/index.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ export type CacheableOptions = {
5050
* The namespace for the cacheable instance. It can be a string or a function that returns a string.
5151
*/
5252
namespace?: string | (() => string);
53+
/**
54+
* The cacheId for the cacheable instance. This is primarily used for the wrap function to not have conflicts.
55+
* If it is not set then it will be a random string that is generated
56+
*/
57+
cacheId?: string;
5358
};
5459

5560
export class Cacheable extends Hookified {
@@ -59,6 +64,7 @@ export class Cacheable extends Hookified {
5964
private _ttl?: number | string;
6065
private readonly _stats = new CacheableStats({enabled: false});
6166
private _namespace?: string | (() => string);
67+
private _cacheId: string = Math.random().toString(36).slice(2);
6268

6369
/**
6470
* Creates a new cacheable instance
@@ -87,6 +93,10 @@ export class Cacheable extends Hookified {
8793
this.setTtl(options.ttl);
8894
}
8995

96+
if (options?.cacheId) {
97+
this._cacheId = options.cacheId;
98+
}
99+
90100
if (options?.namespace) {
91101
this._namespace = options.namespace;
92102
this._primary.namespace = this.getNameSpace();
@@ -225,6 +235,24 @@ export class Cacheable extends Hookified {
225235
this.setTtl(ttl);
226236
}
227237

238+
/**
239+
* The cacheId for the cacheable instance. This is primarily used for the wrap function to not have conflicts.
240+
* If it is not set then it will be a random string that is generated
241+
* @returns {string} The cacheId for the cacheable instance
242+
*/
243+
public get cacheId(): string {
244+
return this._cacheId;
245+
}
246+
247+
/**
248+
* Sets the cacheId for the cacheable instance. This is primarily used for the wrap function to not have conflicts.
249+
* If it is not set then it will be a random string that is generated
250+
* @param {string} cacheId The cacheId for the cacheable instance
251+
*/
252+
public set cacheId(cacheId: string) {
253+
this._cacheId = cacheId;
254+
}
255+
228256
/**
229257
* Sets the primary store for the cacheable instance
230258
* @param {Keyv | KeyvStoreAdapter} primary The primary store for the cacheable instance
@@ -603,6 +631,7 @@ export class Cacheable extends Hookified {
603631
ttl: options?.ttl ?? this._ttl,
604632
keyPrefix: options?.keyPrefix,
605633
cache: this,
634+
cacheId: this._cacheId,
606635
};
607636

608637
return wrap<T>(function_, wrapOptions);

packages/cacheable/src/wrap.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export type WrapFunctionOptions = {
66
ttl?: number | string;
77
keyPrefix?: string;
88
cacheErrors?: boolean;
9+
cacheId?: string;
910
};
1011

1112
export type WrapOptions = WrapFunctionOptions & {
@@ -53,7 +54,9 @@ export function wrap<T>(function_: AnyFunction, options: WrapOptions): AnyFuncti
5354
value = await cache.get(cacheKey) as T | undefined;
5455

5556
if (value === undefined) {
56-
value = await coalesceAsync(cacheKey, async () => {
57+
const cacheId = options.cacheId ?? 'default';
58+
const coalesceKey = `${cacheId}::${cacheKey}`;
59+
value = await coalesceAsync(coalesceKey, async () => {
5760
try {
5861
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
5962
const result = await function_(...arguments_) as T;

packages/cacheable/test/index.test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,13 @@ describe('cacheable options and properties', async () => {
8888
cacheable.ttl = 0;
8989
expect(cacheable.ttl).toEqual(undefined);
9090
});
91+
92+
test('should be able to set the cacheId', async () => {
93+
const cacheable = new Cacheable({cacheId: 'test'});
94+
expect(cacheable.cacheId).toEqual('test');
95+
cacheable.cacheId = 'test2';
96+
expect(cacheable.cacheId).toEqual('test2');
97+
});
9198
});
9299

93100
describe('cacheable stats', async () => {

0 commit comments

Comments
 (0)