@expo/entity-cache-adapter-local-memory 0.50.0 → 0.53.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/src/GenericLocalMemoryCacher.d.ts +29 -8
- package/build/src/GenericLocalMemoryCacher.js +8 -19
- package/build/src/GenericLocalMemoryCacher.js.map +1 -1
- package/build/src/LocalMemoryCacheAdapterProvider.d.ts +5 -6
- package/build/src/LocalMemoryCacheAdapterProvider.js +4 -4
- package/build/src/LocalMemoryCacheAdapterProvider.js.map +1 -1
- package/package.json +7 -8
- package/src/GenericLocalMemoryCacher.ts +41 -25
- package/src/LocalMemoryCacheAdapterProvider.ts +10 -13
- package/src/__testfixtures__/createLocalMemoryTestEntityCompanionProvider.ts +18 -1
- package/src/__tests__/GenericLocalMemoryCacher-test.ts +15 -40
|
@@ -1,17 +1,38 @@
|
|
|
1
1
|
import { CacheLoadResult, EntityConfiguration, IEntityGenericCacher, IEntityLoadKey, IEntityLoadValue } from '@expo/entity';
|
|
2
|
-
import LRUCache from 'lru-cache';
|
|
3
2
|
export declare const DOES_NOT_EXIST_LOCAL_MEMORY_CACHE: unique symbol;
|
|
3
|
+
/**
|
|
4
|
+
* Type of value stored in the local memory cache. This is either the cached fields, or the
|
|
5
|
+
* DOES_NOT_EXIST_LOCAL_MEMORY_CACHE sentinel value.
|
|
6
|
+
*/
|
|
4
7
|
export type LocalMemoryCacheValue<TFields extends Record<string, any>> = Readonly<TFields> | typeof DOES_NOT_EXIST_LOCAL_MEMORY_CACHE;
|
|
5
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Interface for a local memory cache used by GenericLocalMemoryCacher. Most often, this is something like
|
|
10
|
+
* a TTLCache from the `@isaacs/ttlcache` package or an lru-cache.
|
|
11
|
+
*/
|
|
12
|
+
export interface ILocalMemoryCache<TFields extends Record<string, any>> {
|
|
13
|
+
/**
|
|
14
|
+
* Gets a value from the cache for specified key.
|
|
15
|
+
* @param key - key to get
|
|
16
|
+
* @returns the cached value, or undefined if not present
|
|
17
|
+
*/
|
|
18
|
+
get(key: string): LocalMemoryCacheValue<TFields> | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* Sets a value in the cache for specified key.
|
|
21
|
+
* @param key - key to set
|
|
22
|
+
* @param value - value to set
|
|
23
|
+
*/
|
|
24
|
+
set(key: string, value: LocalMemoryCacheValue<TFields>): void;
|
|
25
|
+
/**
|
|
26
|
+
* Deletes a value from the cache for specified key.
|
|
27
|
+
* @param key - key to delete
|
|
28
|
+
*/
|
|
29
|
+
delete(key: string): void;
|
|
30
|
+
}
|
|
6
31
|
export declare class GenericLocalMemoryCacher<TFields extends Record<string, any>, TIDField extends keyof TFields> implements IEntityGenericCacher<TFields, TIDField> {
|
|
7
32
|
private readonly entityConfiguration;
|
|
8
33
|
private readonly localMemoryCache;
|
|
9
|
-
constructor(entityConfiguration: EntityConfiguration<TFields, TIDField>, localMemoryCache:
|
|
10
|
-
static
|
|
11
|
-
maxSize?: number;
|
|
12
|
-
ttlSeconds?: number;
|
|
13
|
-
}): LocalMemoryCache<TFields>;
|
|
14
|
-
static createNoOpCache<TFields extends Record<string, any>>(): LocalMemoryCache<TFields>;
|
|
34
|
+
constructor(entityConfiguration: EntityConfiguration<TFields, TIDField>, localMemoryCache: ILocalMemoryCache<TFields>);
|
|
35
|
+
static createNoOpCache<TFields extends Record<string, any>>(): ILocalMemoryCache<TFields>;
|
|
15
36
|
loadManyAsync(keys: readonly string[]): Promise<ReadonlyMap<string, CacheLoadResult<TFields>>>;
|
|
16
37
|
cacheManyAsync(objectMap: ReadonlyMap<string, Readonly<TFields>>): Promise<void>;
|
|
17
38
|
cacheDBMissesAsync(keys: readonly string[]): Promise<void>;
|
|
@@ -1,11 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.GenericLocalMemoryCacher = exports.DOES_NOT_EXIST_LOCAL_MEMORY_CACHE = void 0;
|
|
7
4
|
const entity_1 = require("@expo/entity");
|
|
8
|
-
const lru_cache_1 = __importDefault(require("lru-cache"));
|
|
9
5
|
// Sentinel value we store in local memory to negatively cache a database miss.
|
|
10
6
|
// The sentinel value is distinct from any (positively) cached value.
|
|
11
7
|
exports.DOES_NOT_EXIST_LOCAL_MEMORY_CACHE = Symbol('doesNotExist');
|
|
@@ -16,21 +12,14 @@ class GenericLocalMemoryCacher {
|
|
|
16
12
|
this.entityConfiguration = entityConfiguration;
|
|
17
13
|
this.localMemoryCache = localMemoryCache;
|
|
18
14
|
}
|
|
19
|
-
static createLRUCache(options = {}) {
|
|
20
|
-
const DEFAULT_LRU_CACHE_MAX_AGE_SECONDS = 10;
|
|
21
|
-
const DEFAULT_LRU_CACHE_SIZE = 10000;
|
|
22
|
-
const maxAgeSeconds = options.ttlSeconds ?? DEFAULT_LRU_CACHE_MAX_AGE_SECONDS;
|
|
23
|
-
return new lru_cache_1.default({
|
|
24
|
-
max: options.maxSize ?? DEFAULT_LRU_CACHE_SIZE,
|
|
25
|
-
length: (value) => (value === exports.DOES_NOT_EXIST_LOCAL_MEMORY_CACHE ? 0 : 1),
|
|
26
|
-
maxAge: maxAgeSeconds * 1000, // convert to ms
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
15
|
static createNoOpCache() {
|
|
30
|
-
return
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
16
|
+
return {
|
|
17
|
+
get(_key) {
|
|
18
|
+
return undefined;
|
|
19
|
+
},
|
|
20
|
+
set(_key, _value) { },
|
|
21
|
+
delete(_key) { },
|
|
22
|
+
};
|
|
34
23
|
}
|
|
35
24
|
async loadManyAsync(keys) {
|
|
36
25
|
const cacheResults = new Map();
|
|
@@ -67,7 +56,7 @@ class GenericLocalMemoryCacher {
|
|
|
67
56
|
}
|
|
68
57
|
async invalidateManyAsync(keys) {
|
|
69
58
|
for (const key of keys) {
|
|
70
|
-
this.localMemoryCache.
|
|
59
|
+
this.localMemoryCache.delete(key);
|
|
71
60
|
}
|
|
72
61
|
}
|
|
73
62
|
makeCacheKeyForStorage(key, value) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GenericLocalMemoryCacher.js","sourceRoot":"","sources":["../../src/GenericLocalMemoryCacher.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"GenericLocalMemoryCacher.js","sourceRoot":"","sources":["../../src/GenericLocalMemoryCacher.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { EntityConfiguration, IEntityCacheAdapter, IEntityCacheAdapterProvider } from '@expo/entity';
|
|
2
|
+
import { ILocalMemoryCache } from './GenericLocalMemoryCacher';
|
|
3
|
+
export type LocalMemoryCacheCreator = <TFields extends Record<string, any>>() => ILocalMemoryCache<TFields>;
|
|
2
4
|
/**
|
|
3
5
|
* Vends local memory cache adapters. An instance of this class may be shared across requests to
|
|
4
6
|
* share the local memory cache.
|
|
@@ -6,16 +8,13 @@ import { EntityConfiguration, IEntityCacheAdapter, IEntityCacheAdapterProvider }
|
|
|
6
8
|
export declare class LocalMemoryCacheAdapterProvider implements IEntityCacheAdapterProvider {
|
|
7
9
|
private readonly localMemoryCacheCreator;
|
|
8
10
|
/**
|
|
9
|
-
* @returns a no-op local memory cache adapter provider, or one that doesn't cache
|
|
11
|
+
* @returns a no-op local memory cache adapter provider, or one that doesn't cache at all.
|
|
10
12
|
*/
|
|
11
13
|
static createNoOpProvider(): IEntityCacheAdapterProvider;
|
|
12
14
|
/**
|
|
13
|
-
* @returns a local memory cache adapter provider configured with the supplied
|
|
15
|
+
* @returns a local memory cache adapter provider configured with the supplied local memory cache creator function.
|
|
14
16
|
*/
|
|
15
|
-
static
|
|
16
|
-
maxSize?: number;
|
|
17
|
-
ttlSeconds?: number;
|
|
18
|
-
}): IEntityCacheAdapterProvider;
|
|
17
|
+
static createProviderWithCacheCreator(cacheCreator: LocalMemoryCacheCreator): IEntityCacheAdapterProvider;
|
|
19
18
|
private readonly localMemoryCacheAdapterMap;
|
|
20
19
|
private constructor();
|
|
21
20
|
getCacheAdapter<TFields extends Record<string, any>, TIDField extends keyof TFields>(entityConfiguration: EntityConfiguration<TFields, TIDField>): IEntityCacheAdapter<TFields, TIDField>;
|
|
@@ -10,16 +10,16 @@ const GenericLocalMemoryCacher_1 = require("./GenericLocalMemoryCacher");
|
|
|
10
10
|
class LocalMemoryCacheAdapterProvider {
|
|
11
11
|
localMemoryCacheCreator;
|
|
12
12
|
/**
|
|
13
|
-
* @returns a no-op local memory cache adapter provider, or one that doesn't cache
|
|
13
|
+
* @returns a no-op local memory cache adapter provider, or one that doesn't cache at all.
|
|
14
14
|
*/
|
|
15
15
|
static createNoOpProvider() {
|
|
16
16
|
return new LocalMemoryCacheAdapterProvider(() => GenericLocalMemoryCacher_1.GenericLocalMemoryCacher.createNoOpCache());
|
|
17
17
|
}
|
|
18
18
|
/**
|
|
19
|
-
* @returns a local memory cache adapter provider configured with the supplied
|
|
19
|
+
* @returns a local memory cache adapter provider configured with the supplied local memory cache creator function.
|
|
20
20
|
*/
|
|
21
|
-
static
|
|
22
|
-
return new LocalMemoryCacheAdapterProvider(
|
|
21
|
+
static createProviderWithCacheCreator(cacheCreator) {
|
|
22
|
+
return new LocalMemoryCacheAdapterProvider(cacheCreator);
|
|
23
23
|
}
|
|
24
24
|
localMemoryCacheAdapterMap = new Map();
|
|
25
25
|
constructor(localMemoryCacheCreator) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LocalMemoryCacheAdapterProvider.js","sourceRoot":"","sources":["../../src/LocalMemoryCacheAdapterProvider.ts"],"names":[],"mappings":";;;AAAA,yCAMsB;AAEtB,
|
|
1
|
+
{"version":3,"file":"LocalMemoryCacheAdapterProvider.js","sourceRoot":"","sources":["../../src/LocalMemoryCacheAdapterProvider.ts"],"names":[],"mappings":";;;AAAA,yCAMsB;AAEtB,yEAAyF;AAKzF;;;GAGG;AACH,MAAa,+BAA+B;IAwBL;IAvBrC;;OAEG;IACH,MAAM,CAAC,kBAAkB;QACvB,OAAO,IAAI,+BAA+B,CAAC,GAAwC,EAAE,CACnF,mDAAwB,CAAC,eAAe,EAAW,CACpD,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,8BAA8B,CACnC,YAAqC;QAErC,OAAO,IAAI,+BAA+B,CAAC,YAAY,CAAC,CAAC;IAC3D,CAAC;IAEgB,0BAA0B,GAAG,IAAI,GAAG,EAGlD,CAAC;IAEJ,YAAqC,uBAAgD;QAAhD,4BAAuB,GAAvB,uBAAuB,CAAyB;IAAG,CAAC;IAElF,eAAe,CACpB,mBAA2D;QAE3D,OAAO,IAAA,wBAAe,EAAC,IAAI,CAAC,0BAA0B,EAAE,mBAAmB,CAAC,SAAS,EAAE,GAAG,EAAE;YAC1F,MAAM,gBAAgB,GAAG,IAAI,CAAC,uBAAuB,EAAW,CAAC;YACjE,OAAO,IAAI,kCAAyB,CAClC,IAAI,mDAAwB,CAAC,mBAAmB,EAAE,gBAAgB,CAAC,CACpE,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC;CACF;AApCD,0EAoCC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@expo/entity-cache-adapter-local-memory",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.53.0",
|
|
4
4
|
"description": "Cross-request local memory cache adapter for @expo/entity",
|
|
5
5
|
"files": [
|
|
6
6
|
"build",
|
|
@@ -28,14 +28,13 @@
|
|
|
28
28
|
"author": "Expo",
|
|
29
29
|
"license": "MIT",
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@expo/entity": "^0.
|
|
32
|
-
"lru-cache": "^6.0.0"
|
|
31
|
+
"@expo/entity": "^0.53.0"
|
|
33
32
|
},
|
|
34
33
|
"devDependencies": {
|
|
35
|
-
"@expo/entity-testing-utils": "^0.
|
|
36
|
-
"@
|
|
37
|
-
"@
|
|
38
|
-
"typescript": "^5.
|
|
34
|
+
"@expo/entity-testing-utils": "^0.53.0",
|
|
35
|
+
"@isaacs/ttlcache": "^2.1.3",
|
|
36
|
+
"@jest/globals": "^30.2.0",
|
|
37
|
+
"typescript": "^5.9.3"
|
|
39
38
|
},
|
|
40
|
-
"gitHead": "
|
|
39
|
+
"gitHead": "833e2e8c55fca79fd78692a37116756a017166e4"
|
|
41
40
|
}
|
|
@@ -6,18 +6,44 @@ import {
|
|
|
6
6
|
IEntityLoadKey,
|
|
7
7
|
IEntityLoadValue,
|
|
8
8
|
} from '@expo/entity';
|
|
9
|
-
import LRUCache from 'lru-cache';
|
|
10
9
|
|
|
11
10
|
// Sentinel value we store in local memory to negatively cache a database miss.
|
|
12
11
|
// The sentinel value is distinct from any (positively) cached value.
|
|
13
12
|
export const DOES_NOT_EXIST_LOCAL_MEMORY_CACHE = Symbol('doesNotExist');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Type of value stored in the local memory cache. This is either the cached fields, or the
|
|
16
|
+
* DOES_NOT_EXIST_LOCAL_MEMORY_CACHE sentinel value.
|
|
17
|
+
*/
|
|
14
18
|
export type LocalMemoryCacheValue<TFields extends Record<string, any>> =
|
|
15
19
|
| Readonly<TFields>
|
|
16
20
|
| typeof DOES_NOT_EXIST_LOCAL_MEMORY_CACHE;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Interface for a local memory cache used by GenericLocalMemoryCacher. Most often, this is something like
|
|
24
|
+
* a TTLCache from the `@isaacs/ttlcache` package or an lru-cache.
|
|
25
|
+
*/
|
|
26
|
+
export interface ILocalMemoryCache<TFields extends Record<string, any>> {
|
|
27
|
+
/**
|
|
28
|
+
* Gets a value from the cache for specified key.
|
|
29
|
+
* @param key - key to get
|
|
30
|
+
* @returns the cached value, or undefined if not present
|
|
31
|
+
*/
|
|
32
|
+
get(key: string): LocalMemoryCacheValue<TFields> | undefined;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Sets a value in the cache for specified key.
|
|
36
|
+
* @param key - key to set
|
|
37
|
+
* @param value - value to set
|
|
38
|
+
*/
|
|
39
|
+
set(key: string, value: LocalMemoryCacheValue<TFields>): void;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Deletes a value from the cache for specified key.
|
|
43
|
+
* @param key - key to delete
|
|
44
|
+
*/
|
|
45
|
+
delete(key: string): void;
|
|
46
|
+
}
|
|
21
47
|
|
|
22
48
|
export class GenericLocalMemoryCacher<
|
|
23
49
|
TFields extends Record<string, any>,
|
|
@@ -26,27 +52,17 @@ export class GenericLocalMemoryCacher<
|
|
|
26
52
|
{
|
|
27
53
|
constructor(
|
|
28
54
|
private readonly entityConfiguration: EntityConfiguration<TFields, TIDField>,
|
|
29
|
-
private readonly localMemoryCache:
|
|
55
|
+
private readonly localMemoryCache: ILocalMemoryCache<TFields>,
|
|
30
56
|
) {}
|
|
31
57
|
|
|
32
|
-
static
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
length: (value) => (value === DOES_NOT_EXIST_LOCAL_MEMORY_CACHE ? 0 : 1),
|
|
41
|
-
maxAge: maxAgeSeconds * 1000, // convert to ms
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
static createNoOpCache<TFields extends Record<string, any>>(): LocalMemoryCache<TFields> {
|
|
46
|
-
return new LRUCache<string, LocalMemoryCacheValue<TFields>>({
|
|
47
|
-
max: 0,
|
|
48
|
-
maxAge: -1,
|
|
49
|
-
});
|
|
58
|
+
static createNoOpCache<TFields extends Record<string, any>>(): ILocalMemoryCache<TFields> {
|
|
59
|
+
return {
|
|
60
|
+
get(_key: string): LocalMemoryCacheValue<TFields> | undefined {
|
|
61
|
+
return undefined;
|
|
62
|
+
},
|
|
63
|
+
set(_key: string, _value: LocalMemoryCacheValue<TFields>): void {},
|
|
64
|
+
delete(_key: string): void {},
|
|
65
|
+
};
|
|
50
66
|
}
|
|
51
67
|
|
|
52
68
|
public async loadManyAsync(
|
|
@@ -87,7 +103,7 @@ export class GenericLocalMemoryCacher<
|
|
|
87
103
|
|
|
88
104
|
public async invalidateManyAsync(keys: readonly string[]): Promise<void> {
|
|
89
105
|
for (const key of keys) {
|
|
90
|
-
this.localMemoryCache.
|
|
106
|
+
this.localMemoryCache.delete(key);
|
|
91
107
|
}
|
|
92
108
|
}
|
|
93
109
|
|
|
@@ -6,15 +6,18 @@ import {
|
|
|
6
6
|
IEntityCacheAdapterProvider,
|
|
7
7
|
} from '@expo/entity';
|
|
8
8
|
|
|
9
|
-
import { GenericLocalMemoryCacher,
|
|
9
|
+
import { GenericLocalMemoryCacher, ILocalMemoryCache } from './GenericLocalMemoryCacher';
|
|
10
10
|
|
|
11
|
+
export type LocalMemoryCacheCreator = <
|
|
12
|
+
TFields extends Record<string, any>,
|
|
13
|
+
>() => ILocalMemoryCache<TFields>;
|
|
11
14
|
/**
|
|
12
15
|
* Vends local memory cache adapters. An instance of this class may be shared across requests to
|
|
13
16
|
* share the local memory cache.
|
|
14
17
|
*/
|
|
15
18
|
export class LocalMemoryCacheAdapterProvider implements IEntityCacheAdapterProvider {
|
|
16
19
|
/**
|
|
17
|
-
* @returns a no-op local memory cache adapter provider, or one that doesn't cache
|
|
20
|
+
* @returns a no-op local memory cache adapter provider, or one that doesn't cache at all.
|
|
18
21
|
*/
|
|
19
22
|
static createNoOpProvider(): IEntityCacheAdapterProvider {
|
|
20
23
|
return new LocalMemoryCacheAdapterProvider(<TFields extends Record<string, any>>() =>
|
|
@@ -23,14 +26,12 @@ export class LocalMemoryCacheAdapterProvider implements IEntityCacheAdapterProvi
|
|
|
23
26
|
}
|
|
24
27
|
|
|
25
28
|
/**
|
|
26
|
-
* @returns a local memory cache adapter provider configured with the supplied
|
|
29
|
+
* @returns a local memory cache adapter provider configured with the supplied local memory cache creator function.
|
|
27
30
|
*/
|
|
28
|
-
static
|
|
29
|
-
|
|
31
|
+
static createProviderWithCacheCreator(
|
|
32
|
+
cacheCreator: LocalMemoryCacheCreator,
|
|
30
33
|
): IEntityCacheAdapterProvider {
|
|
31
|
-
return new LocalMemoryCacheAdapterProvider(
|
|
32
|
-
GenericLocalMemoryCacher.createLRUCache<TFields>(options),
|
|
33
|
-
);
|
|
34
|
+
return new LocalMemoryCacheAdapterProvider(cacheCreator);
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
private readonly localMemoryCacheAdapterMap = new Map<
|
|
@@ -38,11 +39,7 @@ export class LocalMemoryCacheAdapterProvider implements IEntityCacheAdapterProvi
|
|
|
38
39
|
GenericEntityCacheAdapter<any, any>
|
|
39
40
|
>();
|
|
40
41
|
|
|
41
|
-
private constructor(
|
|
42
|
-
private readonly localMemoryCacheCreator: <
|
|
43
|
-
TFields extends Record<string, any>,
|
|
44
|
-
>() => LocalMemoryCache<TFields>,
|
|
45
|
-
) {}
|
|
42
|
+
private constructor(private readonly localMemoryCacheCreator: LocalMemoryCacheCreator) {}
|
|
46
43
|
|
|
47
44
|
public getCacheAdapter<TFields extends Record<string, any>, TIDField extends keyof TFields>(
|
|
48
45
|
entityConfiguration: EntityConfiguration<TFields, TIDField>,
|
|
@@ -4,11 +4,26 @@ import {
|
|
|
4
4
|
NoOpEntityMetricsAdapter,
|
|
5
5
|
} from '@expo/entity';
|
|
6
6
|
import { StubDatabaseAdapterProvider, StubQueryContextProvider } from '@expo/entity-testing-utils';
|
|
7
|
+
import { TTLCache } from '@isaacs/ttlcache';
|
|
7
8
|
|
|
9
|
+
import { ILocalMemoryCache, LocalMemoryCacheValue } from '../GenericLocalMemoryCacher';
|
|
8
10
|
import { LocalMemoryCacheAdapterProvider } from '../LocalMemoryCacheAdapterProvider';
|
|
9
11
|
|
|
10
12
|
const queryContextProvider = new StubQueryContextProvider();
|
|
11
13
|
|
|
14
|
+
function createTTLCache<TFields extends Record<string, any>>(
|
|
15
|
+
options: { maxSize?: number; ttlSeconds?: number } = {},
|
|
16
|
+
): ILocalMemoryCache<TFields> {
|
|
17
|
+
const DEFAULT_LRU_CACHE_MAX_AGE_SECONDS = 10;
|
|
18
|
+
const DEFAULT_LRU_CACHE_SIZE = 10000;
|
|
19
|
+
const maxAgeSeconds = options.ttlSeconds ?? DEFAULT_LRU_CACHE_MAX_AGE_SECONDS;
|
|
20
|
+
return new TTLCache<string, LocalMemoryCacheValue<TFields>>({
|
|
21
|
+
max: options.maxSize ?? DEFAULT_LRU_CACHE_SIZE,
|
|
22
|
+
ttl: maxAgeSeconds * 1000, // convert to ms
|
|
23
|
+
updateAgeOnGet: true,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
12
27
|
export const createLocalMemoryTestEntityCompanionProvider = (
|
|
13
28
|
localMemoryOptions: { maxSize?: number; ttlSeconds?: number } = {},
|
|
14
29
|
metricsAdapter: IEntityMetricsAdapter = new NoOpEntityMetricsAdapter(),
|
|
@@ -16,7 +31,9 @@ export const createLocalMemoryTestEntityCompanionProvider = (
|
|
|
16
31
|
const localMemoryCacheAdapterProvider =
|
|
17
32
|
localMemoryOptions.maxSize === 0 && localMemoryOptions.ttlSeconds === 0
|
|
18
33
|
? LocalMemoryCacheAdapterProvider.createNoOpProvider()
|
|
19
|
-
: LocalMemoryCacheAdapterProvider.
|
|
34
|
+
: LocalMemoryCacheAdapterProvider.createProviderWithCacheCreator(() =>
|
|
35
|
+
createTTLCache(localMemoryOptions),
|
|
36
|
+
);
|
|
20
37
|
return new EntityCompanionProvider(
|
|
21
38
|
metricsAdapter,
|
|
22
39
|
new Map([
|
|
@@ -7,11 +7,14 @@ import {
|
|
|
7
7
|
SingleFieldValueHolderMap,
|
|
8
8
|
UUIDField,
|
|
9
9
|
} from '@expo/entity';
|
|
10
|
+
import { TTLCache } from '@isaacs/ttlcache';
|
|
10
11
|
import { describe, expect, it } from '@jest/globals';
|
|
11
12
|
|
|
12
13
|
import {
|
|
13
14
|
DOES_NOT_EXIST_LOCAL_MEMORY_CACHE,
|
|
14
15
|
GenericLocalMemoryCacher,
|
|
16
|
+
ILocalMemoryCache,
|
|
17
|
+
LocalMemoryCacheValue,
|
|
15
18
|
} from '../GenericLocalMemoryCacher';
|
|
16
19
|
|
|
17
20
|
type BlahFields = {
|
|
@@ -28,41 +31,19 @@ const entityConfiguration = new EntityConfiguration<BlahFields, 'id'>({
|
|
|
28
31
|
cacheAdapterFlavor: 'local-memory',
|
|
29
32
|
});
|
|
30
33
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
expect(cache.maxAge).toBe(10000);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it('respects specified options', () => {
|
|
40
|
-
const cache = GenericLocalMemoryCacher.createLRUCache({
|
|
41
|
-
ttlSeconds: 3,
|
|
42
|
-
maxSize: 10,
|
|
43
|
-
});
|
|
44
|
-
expect(cache.max).toBe(10);
|
|
45
|
-
expect(cache.maxAge).toBe(3000);
|
|
46
|
-
});
|
|
34
|
+
function createTTLCache<TFields extends Record<string, any>>(): ILocalMemoryCache<TFields> {
|
|
35
|
+
return new TTLCache<string, LocalMemoryCacheValue<TFields>>({
|
|
36
|
+
max: 10000,
|
|
37
|
+
ttl: 10 * 1000, // convert to ms
|
|
38
|
+
updateAgeOnGet: true,
|
|
47
39
|
});
|
|
48
|
-
|
|
49
|
-
describe(GenericLocalMemoryCacher.createNoOpCache, () => {
|
|
50
|
-
it('creates a no-op cache', () => {
|
|
51
|
-
const cache = GenericLocalMemoryCacher.createNoOpCache<{ hello: 'world' }>();
|
|
52
|
-
cache.set('a', { hello: 'world' });
|
|
53
|
-
expect(cache.get('a')).toBeUndefined();
|
|
54
|
-
});
|
|
55
|
-
});
|
|
56
|
-
});
|
|
40
|
+
}
|
|
57
41
|
|
|
58
42
|
describe('Use within GenericEntityCacheAdapter', () => {
|
|
59
43
|
describe('loadManyAsync', () => {
|
|
60
44
|
it('returns appropriate cache results', async () => {
|
|
61
45
|
const cacheAdapter = new GenericEntityCacheAdapter(
|
|
62
|
-
new GenericLocalMemoryCacher(
|
|
63
|
-
entityConfiguration,
|
|
64
|
-
GenericLocalMemoryCacher.createLRUCache(),
|
|
65
|
-
),
|
|
46
|
+
new GenericLocalMemoryCacher(entityConfiguration, createTTLCache()),
|
|
66
47
|
);
|
|
67
48
|
|
|
68
49
|
const cacheHits = new Map<SingleFieldValueHolder<BlahFields, 'id'>, Readonly<BlahFields>>([
|
|
@@ -97,10 +78,7 @@ describe('Use within GenericEntityCacheAdapter', () => {
|
|
|
97
78
|
|
|
98
79
|
it('returns empty map when passed empty array of values', async () => {
|
|
99
80
|
const cacheAdapter = new GenericEntityCacheAdapter(
|
|
100
|
-
new GenericLocalMemoryCacher(
|
|
101
|
-
entityConfiguration,
|
|
102
|
-
GenericLocalMemoryCacher.createLRUCache(),
|
|
103
|
-
),
|
|
81
|
+
new GenericLocalMemoryCacher(entityConfiguration, createTTLCache()),
|
|
104
82
|
);
|
|
105
83
|
const results = await cacheAdapter.loadManyAsync(
|
|
106
84
|
new SingleFieldHolder<BlahFields, 'id', 'id'>('id'),
|
|
@@ -112,7 +90,7 @@ describe('Use within GenericEntityCacheAdapter', () => {
|
|
|
112
90
|
|
|
113
91
|
describe('cacheManyAsync', () => {
|
|
114
92
|
it('correctly caches all objects', async () => {
|
|
115
|
-
const localMemoryCache =
|
|
93
|
+
const localMemoryCache = createTTLCache<BlahFields>();
|
|
116
94
|
|
|
117
95
|
const localMemoryCacher = new GenericLocalMemoryCacher(entityConfiguration, localMemoryCache);
|
|
118
96
|
const cacheAdapter = new GenericEntityCacheAdapter(localMemoryCacher);
|
|
@@ -133,7 +111,7 @@ describe('Use within GenericEntityCacheAdapter', () => {
|
|
|
133
111
|
|
|
134
112
|
describe('cacheDBMissesAsync', () => {
|
|
135
113
|
it('correctly caches misses', async () => {
|
|
136
|
-
const localMemoryCache =
|
|
114
|
+
const localMemoryCache = createTTLCache<BlahFields>();
|
|
137
115
|
|
|
138
116
|
const localMemoryCacher = new GenericLocalMemoryCacher(entityConfiguration, localMemoryCache);
|
|
139
117
|
const cacheAdapter = new GenericEntityCacheAdapter(localMemoryCacher);
|
|
@@ -151,7 +129,7 @@ describe('Use within GenericEntityCacheAdapter', () => {
|
|
|
151
129
|
|
|
152
130
|
describe('invalidateManyAsync', () => {
|
|
153
131
|
it('invalidates correctly', async () => {
|
|
154
|
-
const localMemoryCache =
|
|
132
|
+
const localMemoryCache = createTTLCache<BlahFields>();
|
|
155
133
|
|
|
156
134
|
const cacheAdapter = new GenericEntityCacheAdapter(
|
|
157
135
|
new GenericLocalMemoryCacher(entityConfiguration, localMemoryCache),
|
|
@@ -182,10 +160,7 @@ describe('Use within GenericEntityCacheAdapter', () => {
|
|
|
182
160
|
|
|
183
161
|
it('returns when passed empty array of values', async () => {
|
|
184
162
|
const cacheAdapter = new GenericEntityCacheAdapter(
|
|
185
|
-
new GenericLocalMemoryCacher(
|
|
186
|
-
entityConfiguration,
|
|
187
|
-
GenericLocalMemoryCacher.createLRUCache<BlahFields>({}),
|
|
188
|
-
),
|
|
163
|
+
new GenericLocalMemoryCacher(entityConfiguration, createTTLCache<BlahFields>()),
|
|
189
164
|
);
|
|
190
165
|
await cacheAdapter.invalidateManyAsync(
|
|
191
166
|
new SingleFieldHolder<BlahFields, 'id', 'id'>('id'),
|