@expo/entity-cache-adapter-redis 0.40.0 → 0.42.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.
Files changed (49) hide show
  1. package/build/GenericRedisCacher.d.ts +26 -4
  2. package/build/GenericRedisCacher.js +36 -5
  3. package/build/GenericRedisCacher.js.map +1 -1
  4. package/build/RedisCacheAdapterProvider.d.ts +1 -1
  5. package/build/RedisCacheAdapterProvider.js.map +1 -1
  6. package/build/index.d.ts +1 -0
  7. package/build/index.js +1 -0
  8. package/build/index.js.map +1 -1
  9. package/build/tsconfig.build.tsbuildinfo +1 -0
  10. package/build/utils/getSurroundingCacheKeyVersionsForInvalidation.d.ts +1 -0
  11. package/build/utils/getSurroundingCacheKeyVersionsForInvalidation.js +11 -0
  12. package/build/utils/getSurroundingCacheKeyVersionsForInvalidation.js.map +1 -0
  13. package/package.json +9 -7
  14. package/src/GenericRedisCacher.ts +72 -13
  15. package/src/RedisCacheAdapterProvider.ts +3 -3
  16. package/src/__integration-tests__/BatchedRedisCacheAdapter-integration-test.ts +48 -29
  17. package/src/__integration-tests__/GenericRedisCacher-full-integration-test.ts +104 -45
  18. package/src/__integration-tests__/GenericRedisCacher-integration-test.ts +7 -5
  19. package/src/__integration-tests__/errors-test.ts +8 -4
  20. package/src/{testfixtures → __testfixtures__}/RedisTestEntity.ts +13 -29
  21. package/src/{testfixtures → __testfixtures__}/createRedisIntegrationTestEntityCompanionProvider.ts +1 -2
  22. package/src/__tests__/GenericRedisCacher-test.ts +68 -12
  23. package/src/index.ts +1 -0
  24. package/src/utils/__tests__/getSurroundingCacheKeyVersionsForInvalidation-test.ts +9 -0
  25. package/src/utils/getSurroundingCacheKeyVersionsForInvalidation.ts +9 -0
  26. package/build/__integration-tests__/BatchedRedisCacheAdapter-integration-test.d.ts +0 -1
  27. package/build/__integration-tests__/BatchedRedisCacheAdapter-integration-test.js +0 -133
  28. package/build/__integration-tests__/BatchedRedisCacheAdapter-integration-test.js.map +0 -1
  29. package/build/__integration-tests__/GenericRedisCacher-full-integration-test.d.ts +0 -1
  30. package/build/__integration-tests__/GenericRedisCacher-full-integration-test.js +0 -110
  31. package/build/__integration-tests__/GenericRedisCacher-full-integration-test.js.map +0 -1
  32. package/build/__integration-tests__/GenericRedisCacher-integration-test.d.ts +0 -1
  33. package/build/__integration-tests__/GenericRedisCacher-integration-test.js +0 -124
  34. package/build/__integration-tests__/GenericRedisCacher-integration-test.js.map +0 -1
  35. package/build/__integration-tests__/errors-test.d.ts +0 -1
  36. package/build/__integration-tests__/errors-test.js +0 -39
  37. package/build/__integration-tests__/errors-test.js.map +0 -1
  38. package/build/__tests__/GenericRedisCacher-test.d.ts +0 -1
  39. package/build/__tests__/GenericRedisCacher-test.js +0 -141
  40. package/build/__tests__/GenericRedisCacher-test.js.map +0 -1
  41. package/build/errors/__tests__/wrapNativeRedisCallAsync-test.d.ts +0 -1
  42. package/build/errors/__tests__/wrapNativeRedisCallAsync-test.js +0 -42
  43. package/build/errors/__tests__/wrapNativeRedisCallAsync-test.js.map +0 -1
  44. package/build/testfixtures/RedisTestEntity.d.ts +0 -16
  45. package/build/testfixtures/RedisTestEntity.js +0 -49
  46. package/build/testfixtures/RedisTestEntity.js.map +0 -1
  47. package/build/testfixtures/createRedisIntegrationTestEntityCompanionProvider.d.ts +0 -3
  48. package/build/testfixtures/createRedisIntegrationTestEntityCompanionProvider.js +0 -31
  49. package/build/testfixtures/createRedisIntegrationTestEntityCompanionProvider.js.map +0 -1
@@ -1,4 +1,4 @@
1
- import { CacheLoadResult, EntityConfiguration, IEntityGenericCacher } from '@expo/entity';
1
+ import { CacheLoadResult, EntityConfiguration, IEntityGenericCacher, IEntityLoadKey, IEntityLoadValue } from '@expo/entity';
2
2
  export interface IRedisTransaction {
3
3
  set(key: string, value: string, secondsToken: 'EX', seconds: number): this;
4
4
  exec(): Promise<any>;
@@ -8,6 +8,22 @@ export interface IRedis {
8
8
  multi(): IRedisTransaction;
9
9
  del(...args: [...keys: string[]]): Promise<any>;
10
10
  }
11
+ /**
12
+ * The strategy for generating the set of cache keys to invalidate in the Redis cache after entity mutation.
13
+ */
14
+ export declare enum RedisCacheInvalidationStrategy {
15
+ /**
16
+ * Invalidate just the cache key(s) for the current cacheKeyVersion of the entity.
17
+ */
18
+ CURRENT_CACHE_KEY_VERSION = "current-cache-key-version",
19
+ /**
20
+ * Invalidate the cache key(s) for the current cacheKeyVersion and the surrounding cache key versions
21
+ * (e.g. `1`, `2` and `3` if the current version is `2`). This can be useful for deployment safety, where
22
+ * some machines may be operating on an old version of the code and thus an old cacheKeyVersion, and some the new version.
23
+ * This strategy generates cache keys for both old and potential future new versions.
24
+ */
25
+ SURROUNDING_CACHE_KEY_VERSIONS = "surrounding-cache-key-versions"
26
+ }
11
27
  export interface GenericRedisCacheContext {
12
28
  /**
13
29
  * Instance of ioredis.Redis
@@ -33,14 +49,20 @@ export interface GenericRedisCacheContext {
33
49
  * this TTL will be assumed not present in the database (unless invalidated).
34
50
  */
35
51
  ttlSecondsNegative: number;
52
+ /**
53
+ * Invalidation strategy for the cache.
54
+ */
55
+ invalidationStrategy: RedisCacheInvalidationStrategy;
36
56
  }
37
- export default class GenericRedisCacher<TFields extends Record<string, any>> implements IEntityGenericCacher<TFields> {
57
+ export default class GenericRedisCacher<TFields extends Record<string, any>, TIDField extends keyof TFields> implements IEntityGenericCacher<TFields, TIDField> {
38
58
  private readonly context;
39
59
  private readonly entityConfiguration;
40
- constructor(context: GenericRedisCacheContext, entityConfiguration: EntityConfiguration<TFields>);
60
+ constructor(context: GenericRedisCacheContext, entityConfiguration: EntityConfiguration<TFields, TIDField>);
41
61
  loadManyAsync(keys: readonly string[]): Promise<ReadonlyMap<string, CacheLoadResult<TFields>>>;
42
62
  cacheManyAsync(objectMap: ReadonlyMap<string, Readonly<TFields>>): Promise<void>;
43
63
  cacheDBMissesAsync(keys: readonly string[]): Promise<void>;
44
64
  invalidateManyAsync(keys: readonly string[]): Promise<void>;
45
- makeCacheKey<N extends keyof TFields>(fieldName: N, fieldValue: NonNullable<TFields[N]>): string;
65
+ private makeCacheKeyForCacheKeyVersion;
66
+ makeCacheKeyForStorage<TLoadKey extends IEntityLoadKey<TFields, TIDField, TSerializedLoadValue, TLoadValue>, TSerializedLoadValue, TLoadValue extends IEntityLoadValue<TSerializedLoadValue>>(key: TLoadKey, value: TLoadValue): string;
67
+ makeCacheKeysForInvalidation<TLoadKey extends IEntityLoadKey<TFields, TIDField, TSerializedLoadValue, TLoadValue>, TSerializedLoadValue, TLoadValue extends IEntityLoadValue<TSerializedLoadValue>>(key: TLoadKey, value: TLoadValue): readonly string[];
46
68
  }
@@ -3,13 +3,31 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.RedisCacheInvalidationStrategy = void 0;
6
7
  const entity_1 = require("@expo/entity");
7
- const invariant_1 = __importDefault(require("invariant"));
8
8
  const RedisCommon_1 = require("./RedisCommon");
9
9
  const wrapNativeRedisCallAsync_1 = __importDefault(require("./errors/wrapNativeRedisCallAsync"));
10
+ const getSurroundingCacheKeyVersionsForInvalidation_1 = require("./utils/getSurroundingCacheKeyVersionsForInvalidation");
10
11
  // Sentinel value we store in Redis to negatively cache a database miss.
11
12
  // The sentinel value is distinct from any (positively) cached value.
12
13
  const DOES_NOT_EXIST_REDIS = '';
14
+ /**
15
+ * The strategy for generating the set of cache keys to invalidate in the Redis cache after entity mutation.
16
+ */
17
+ var RedisCacheInvalidationStrategy;
18
+ (function (RedisCacheInvalidationStrategy) {
19
+ /**
20
+ * Invalidate just the cache key(s) for the current cacheKeyVersion of the entity.
21
+ */
22
+ RedisCacheInvalidationStrategy["CURRENT_CACHE_KEY_VERSION"] = "current-cache-key-version";
23
+ /**
24
+ * Invalidate the cache key(s) for the current cacheKeyVersion and the surrounding cache key versions
25
+ * (e.g. `1`, `2` and `3` if the current version is `2`). This can be useful for deployment safety, where
26
+ * some machines may be operating on an old version of the code and thus an old cacheKeyVersion, and some the new version.
27
+ * This strategy generates cache keys for both old and potential future new versions.
28
+ */
29
+ RedisCacheInvalidationStrategy["SURROUNDING_CACHE_KEY_VERSIONS"] = "surrounding-cache-key-versions";
30
+ })(RedisCacheInvalidationStrategy || (exports.RedisCacheInvalidationStrategy = RedisCacheInvalidationStrategy = {}));
13
31
  class GenericRedisCacher {
14
32
  context;
15
33
  entityConfiguration;
@@ -71,10 +89,23 @@ class GenericRedisCacher {
71
89
  }
72
90
  await (0, wrapNativeRedisCallAsync_1.default)(() => this.context.redisClient.del(...keys));
73
91
  }
74
- makeCacheKey(fieldName, fieldValue) {
75
- const columnName = this.entityConfiguration.entityToDBFieldsKeyMapping.get(fieldName);
76
- (0, invariant_1.default)(columnName, `database field mapping missing for ${String(fieldName)}`);
77
- return this.context.makeKeyFn(this.context.cacheKeyPrefix, this.entityConfiguration.tableName, `v2.${this.entityConfiguration.cacheKeyVersion}`, columnName, String(fieldValue));
92
+ makeCacheKeyForCacheKeyVersion(key, value, cacheKeyVersion) {
93
+ const cacheKeyType = key.getLoadMethodType();
94
+ const parts = key.createCacheKeyPartsForLoadValue(this.entityConfiguration, value);
95
+ return this.context.makeKeyFn(this.context.cacheKeyPrefix, cacheKeyType, this.entityConfiguration.tableName, `v2.${cacheKeyVersion}`, ...parts);
96
+ }
97
+ makeCacheKeyForStorage(key, value) {
98
+ return this.makeCacheKeyForCacheKeyVersion(key, value, this.entityConfiguration.cacheKeyVersion);
99
+ }
100
+ makeCacheKeysForInvalidation(key, value) {
101
+ switch (this.context.invalidationStrategy) {
102
+ case RedisCacheInvalidationStrategy.CURRENT_CACHE_KEY_VERSION:
103
+ return [
104
+ this.makeCacheKeyForCacheKeyVersion(key, value, this.entityConfiguration.cacheKeyVersion),
105
+ ];
106
+ case RedisCacheInvalidationStrategy.SURROUNDING_CACHE_KEY_VERSIONS:
107
+ return (0, getSurroundingCacheKeyVersionsForInvalidation_1.getSurroundingCacheKeyVersionsForInvalidation)(this.entityConfiguration.cacheKeyVersion).map((cacheKeyVersion) => this.makeCacheKeyForCacheKeyVersion(key, value, cacheKeyVersion));
108
+ }
78
109
  }
79
110
  }
80
111
  exports.default = GenericRedisCacher;
@@ -1 +1 @@
1
- {"version":3,"file":"GenericRedisCacher.js","sourceRoot":"","sources":["../src/GenericRedisCacher.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"GenericRedisCacher.js","sourceRoot":"","sources":["../src/GenericRedisCacher.ts"],"names":[],"mappings":"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"}
@@ -3,5 +3,5 @@ import { GenericRedisCacheContext } from './GenericRedisCacher';
3
3
  export default class RedisCacheAdapterProvider implements IEntityCacheAdapterProvider {
4
4
  private readonly context;
5
5
  constructor(context: GenericRedisCacheContext);
6
- getCacheAdapter<TFields extends Record<string, any>>(entityConfiguration: EntityConfiguration<TFields>): IEntityCacheAdapter<TFields>;
6
+ getCacheAdapter<TFields extends Record<string, any>, TIDField extends keyof TFields>(entityConfiguration: EntityConfiguration<TFields, TIDField>): IEntityCacheAdapter<TFields, TIDField>;
7
7
  }
@@ -1 +1 @@
1
- {"version":3,"file":"RedisCacheAdapterProvider.js","sourceRoot":"","sources":["../src/RedisCacheAdapterProvider.ts"],"names":[],"mappings":";;;;;AAAA,yCAKsB;AAEtB,8EAAoF;AAEpF,MAAqB,yBAAyB;IACf;IAA7B,YAA6B,OAAiC;QAAjC,YAAO,GAAP,OAAO,CAA0B;IAAG,CAAC;IAElE,eAAe,CACb,mBAAiD;QAEjD,OAAO,IAAI,kCAAyB,CAAC,IAAI,4BAAkB,CAAC,IAAI,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAC,CAAC;IAClG,CAAC;CACF;AARD,4CAQC"}
1
+ {"version":3,"file":"RedisCacheAdapterProvider.js","sourceRoot":"","sources":["../src/RedisCacheAdapterProvider.ts"],"names":[],"mappings":";;;;;AAAA,yCAKsB;AAEtB,8EAAoF;AAEpF,MAAqB,yBAAyB;IACf;IAA7B,YAA6B,OAAiC;QAAjC,YAAO,GAAP,OAAO,CAA0B;IAAG,CAAC;IAElE,eAAe,CACb,mBAA2D;QAE3D,OAAO,IAAI,kCAAyB,CAAC,IAAI,4BAAkB,CAAC,IAAI,CAAC,OAAO,EAAE,mBAAmB,CAAC,CAAC,CAAC;IAClG,CAAC;CACF;AARD,4CAQC"}
package/build/index.d.ts CHANGED
@@ -7,3 +7,4 @@ export * from './GenericRedisCacher';
7
7
  export { default as RedisCacheAdapterProvider } from './RedisCacheAdapterProvider';
8
8
  export * from './RedisCommon';
9
9
  export { default as wrapNativeRedisCallAsync } from './errors/wrapNativeRedisCallAsync';
10
+ export * from './utils/getSurroundingCacheKeyVersionsForInvalidation';
package/build/index.js CHANGED
@@ -31,4 +31,5 @@ Object.defineProperty(exports, "RedisCacheAdapterProvider", { enumerable: true,
31
31
  __exportStar(require("./RedisCommon"), exports);
32
32
  var wrapNativeRedisCallAsync_1 = require("./errors/wrapNativeRedisCallAsync");
33
33
  Object.defineProperty(exports, "wrapNativeRedisCallAsync", { enumerable: true, get: function () { return __importDefault(wrapNativeRedisCallAsync_1).default; } });
34
+ __exportStar(require("./utils/getSurroundingCacheKeyVersionsForInvalidation"), exports);
34
35
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,iCAAiC;AACjC;;;GAGG;;;;;;;;;;;;;;;;;;;;AAEH,2DAAqE;AAA5D,yIAAA,OAAO,OAAsB;AACtC,uDAAqC;AACrC,yEAAmF;AAA1E,uJAAA,OAAO,OAA6B;AAC7C,gDAA8B;AAC9B,8EAAwF;AAA/E,qJAAA,OAAO,OAA4B"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,iCAAiC;AACjC;;;GAGG;;;;;;;;;;;;;;;;;;;;AAEH,2DAAqE;AAA5D,yIAAA,OAAO,OAAsB;AACtC,uDAAqC;AACrC,yEAAmF;AAA1E,uJAAA,OAAO,OAA6B;AAC7C,gDAA8B;AAC9B,8EAAwF;AAA/E,qJAAA,OAAO,OAA4B;AAC5C,wFAAsE"}
@@ -0,0 +1 @@
1
+ {"root":["../src/genericrediscacher.ts","../src/rediscacheadapterprovider.ts","../src/rediscommon.ts","../src/index.ts","../src/errors/wrapnativerediscallasync.ts","../src/utils/getsurroundingcachekeyversionsforinvalidation.ts"],"version":"5.8.3"}
@@ -0,0 +1 @@
1
+ export declare function getSurroundingCacheKeyVersionsForInvalidation(cacheKeyVersion: number): readonly number[];
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getSurroundingCacheKeyVersionsForInvalidation = getSurroundingCacheKeyVersionsForInvalidation;
4
+ function getSurroundingCacheKeyVersionsForInvalidation(cacheKeyVersion) {
5
+ return [
6
+ ...(cacheKeyVersion === 0 ? [] : [cacheKeyVersion - 1]),
7
+ cacheKeyVersion,
8
+ cacheKeyVersion + 1,
9
+ ];
10
+ }
11
+ //# sourceMappingURL=getSurroundingCacheKeyVersionsForInvalidation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getSurroundingCacheKeyVersionsForInvalidation.js","sourceRoot":"","sources":["../../src/utils/getSurroundingCacheKeyVersionsForInvalidation.ts"],"names":[],"mappings":";;AAAA,sGAQC;AARD,SAAgB,6CAA6C,CAC3D,eAAuB;IAEvB,OAAO;QACL,GAAG,CAAC,eAAe,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,eAAe,GAAG,CAAC,CAAC,CAAC;QACvD,eAAe;QACf,eAAe,GAAG,CAAC;KACpB,CAAC;AACJ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@expo/entity-cache-adapter-redis",
3
- "version": "0.40.0",
3
+ "version": "0.42.0",
4
4
  "description": "Redis cache adapter for @expo/entity",
5
5
  "files": [
6
6
  "build",
@@ -10,6 +10,7 @@
10
10
  "types": "build/index.d.ts",
11
11
  "scripts": {
12
12
  "tsc": "tsc",
13
+ "build": "tsc -b tsconfig.build.json",
13
14
  "clean": "rm -rf build coverage coverage-integration",
14
15
  "lint": "eslint src",
15
16
  "lint-fix": "eslint src --fix",
@@ -27,26 +28,27 @@
27
28
  "author": "Expo",
28
29
  "license": "MIT",
29
30
  "dependencies": {
30
- "@expo/entity": "^0.40.0"
31
+ "@expo/entity": "^0.42.0"
31
32
  },
32
33
  "peerDependencies": {
33
34
  "ioredis": ">=5"
34
35
  },
35
36
  "devDependencies": {
36
37
  "@expo/batcher": "^1.0.0",
37
- "@types/jest": "^29.5.12",
38
+ "@expo/entity-testing-utils": "^0.42.0",
39
+ "@types/jest": "^29.5.14",
38
40
  "@types/node": "^20.14.1",
39
41
  "ctix": "^2.7.0",
40
42
  "eslint": "^8.57.1",
41
43
  "eslint-config-universe": "^14.0.0",
42
44
  "eslint-plugin-tsdoc": "^0.3.0",
43
- "ioredis": "^5.4.1",
45
+ "ioredis": "^5.6.0",
44
46
  "jest": "^29.7.0",
45
47
  "prettier": "^3.3.3",
46
48
  "prettier-plugin-organize-imports": "^4.1.0",
47
- "ts-jest": "^29.2.5",
49
+ "ts-jest": "^29.3.1",
48
50
  "ts-mockito": "^2.6.1",
49
- "typescript": "^5.7.3"
51
+ "typescript": "^5.8.3"
50
52
  },
51
- "gitHead": "74c396255c8177a87cf80da2608072f5989dd519"
53
+ "gitHead": "8414d96d948882735687da146e84913397cd8368"
52
54
  }
@@ -5,11 +5,13 @@ import {
5
5
  transformCacheObjectToFields,
6
6
  transformFieldsToCacheObject,
7
7
  IEntityGenericCacher,
8
+ IEntityLoadKey,
9
+ IEntityLoadValue,
8
10
  } from '@expo/entity';
9
- import invariant from 'invariant';
10
11
 
11
12
  import { redisTransformerMap } from './RedisCommon';
12
13
  import wrapNativeRedisCallAsync from './errors/wrapNativeRedisCallAsync';
14
+ import { getSurroundingCacheKeyVersionsForInvalidation } from './utils/getSurroundingCacheKeyVersionsForInvalidation';
13
15
 
14
16
  // Sentinel value we store in Redis to negatively cache a database miss.
15
17
  // The sentinel value is distinct from any (positively) cached value.
@@ -26,6 +28,24 @@ export interface IRedis {
26
28
  del(...args: [...keys: string[]]): Promise<any>;
27
29
  }
28
30
 
31
+ /**
32
+ * The strategy for generating the set of cache keys to invalidate in the Redis cache after entity mutation.
33
+ */
34
+ export enum RedisCacheInvalidationStrategy {
35
+ /**
36
+ * Invalidate just the cache key(s) for the current cacheKeyVersion of the entity.
37
+ */
38
+ CURRENT_CACHE_KEY_VERSION = 'current-cache-key-version',
39
+
40
+ /**
41
+ * Invalidate the cache key(s) for the current cacheKeyVersion and the surrounding cache key versions
42
+ * (e.g. `1`, `2` and `3` if the current version is `2`). This can be useful for deployment safety, where
43
+ * some machines may be operating on an old version of the code and thus an old cacheKeyVersion, and some the new version.
44
+ * This strategy generates cache keys for both old and potential future new versions.
45
+ */
46
+ SURROUNDING_CACHE_KEY_VERSIONS = 'surrounding-cache-key-versions',
47
+ }
48
+
29
49
  export interface GenericRedisCacheContext {
30
50
  /**
31
51
  * Instance of ioredis.Redis
@@ -55,14 +75,21 @@ export interface GenericRedisCacheContext {
55
75
  * this TTL will be assumed not present in the database (unless invalidated).
56
76
  */
57
77
  ttlSecondsNegative: number;
78
+
79
+ /**
80
+ * Invalidation strategy for the cache.
81
+ */
82
+ invalidationStrategy: RedisCacheInvalidationStrategy;
58
83
  }
59
84
 
60
- export default class GenericRedisCacher<TFields extends Record<string, any>>
61
- implements IEntityGenericCacher<TFields>
85
+ export default class GenericRedisCacher<
86
+ TFields extends Record<string, any>,
87
+ TIDField extends keyof TFields,
88
+ > implements IEntityGenericCacher<TFields, TIDField>
62
89
  {
63
90
  constructor(
64
91
  private readonly context: GenericRedisCacheContext,
65
- private readonly entityConfiguration: EntityConfiguration<TFields>,
92
+ private readonly entityConfiguration: EntityConfiguration<TFields, TIDField>,
66
93
  ) {}
67
94
 
68
95
  public async loadManyAsync(
@@ -147,18 +174,50 @@ export default class GenericRedisCacher<TFields extends Record<string, any>>
147
174
  await wrapNativeRedisCallAsync(() => this.context.redisClient.del(...keys));
148
175
  }
149
176
 
150
- public makeCacheKey<N extends keyof TFields>(
151
- fieldName: N,
152
- fieldValue: NonNullable<TFields[N]>,
153
- ): string {
154
- const columnName = this.entityConfiguration.entityToDBFieldsKeyMapping.get(fieldName);
155
- invariant(columnName, `database field mapping missing for ${String(fieldName)}`);
177
+ private makeCacheKeyForCacheKeyVersion<
178
+ TLoadKey extends IEntityLoadKey<TFields, TIDField, TSerializedLoadValue, TLoadValue>,
179
+ TSerializedLoadValue,
180
+ TLoadValue extends IEntityLoadValue<TSerializedLoadValue>,
181
+ >(key: TLoadKey, value: TLoadValue, cacheKeyVersion: number): string {
182
+ const cacheKeyType = key.getLoadMethodType();
183
+ const parts = key.createCacheKeyPartsForLoadValue(this.entityConfiguration, value);
156
184
  return this.context.makeKeyFn(
157
185
  this.context.cacheKeyPrefix,
186
+ cacheKeyType,
158
187
  this.entityConfiguration.tableName,
159
- `v2.${this.entityConfiguration.cacheKeyVersion}`,
160
- columnName,
161
- String(fieldValue),
188
+ `v2.${cacheKeyVersion}`,
189
+ ...parts,
190
+ );
191
+ }
192
+
193
+ public makeCacheKeyForStorage<
194
+ TLoadKey extends IEntityLoadKey<TFields, TIDField, TSerializedLoadValue, TLoadValue>,
195
+ TSerializedLoadValue,
196
+ TLoadValue extends IEntityLoadValue<TSerializedLoadValue>,
197
+ >(key: TLoadKey, value: TLoadValue): string {
198
+ return this.makeCacheKeyForCacheKeyVersion(
199
+ key,
200
+ value,
201
+ this.entityConfiguration.cacheKeyVersion,
162
202
  );
163
203
  }
204
+
205
+ public makeCacheKeysForInvalidation<
206
+ TLoadKey extends IEntityLoadKey<TFields, TIDField, TSerializedLoadValue, TLoadValue>,
207
+ TSerializedLoadValue,
208
+ TLoadValue extends IEntityLoadValue<TSerializedLoadValue>,
209
+ >(key: TLoadKey, value: TLoadValue): readonly string[] {
210
+ switch (this.context.invalidationStrategy) {
211
+ case RedisCacheInvalidationStrategy.CURRENT_CACHE_KEY_VERSION:
212
+ return [
213
+ this.makeCacheKeyForCacheKeyVersion(key, value, this.entityConfiguration.cacheKeyVersion),
214
+ ];
215
+ case RedisCacheInvalidationStrategy.SURROUNDING_CACHE_KEY_VERSIONS:
216
+ return getSurroundingCacheKeyVersionsForInvalidation(
217
+ this.entityConfiguration.cacheKeyVersion,
218
+ ).map((cacheKeyVersion) =>
219
+ this.makeCacheKeyForCacheKeyVersion(key, value, cacheKeyVersion),
220
+ );
221
+ }
222
+ }
164
223
  }
@@ -10,9 +10,9 @@ import GenericRedisCacher, { GenericRedisCacheContext } from './GenericRedisCach
10
10
  export default class RedisCacheAdapterProvider implements IEntityCacheAdapterProvider {
11
11
  constructor(private readonly context: GenericRedisCacheContext) {}
12
12
 
13
- getCacheAdapter<TFields extends Record<string, any>>(
14
- entityConfiguration: EntityConfiguration<TFields>,
15
- ): IEntityCacheAdapter<TFields> {
13
+ getCacheAdapter<TFields extends Record<string, any>, TIDField extends keyof TFields>(
14
+ entityConfiguration: EntityConfiguration<TFields, TIDField>,
15
+ ): IEntityCacheAdapter<TFields, TIDField> {
16
16
  return new GenericEntityCacheAdapter(new GenericRedisCacher(this.context, entityConfiguration));
17
17
  }
18
18
  }
@@ -1,5 +1,11 @@
1
1
  import { Batcher } from '@expo/batcher';
2
- import { ViewerContext, zipToMap } from '@expo/entity';
2
+ import {
3
+ IEntityGenericCacher,
4
+ SingleFieldHolder,
5
+ SingleFieldValueHolder,
6
+ ViewerContext,
7
+ zipToMap,
8
+ } from '@expo/entity';
3
9
  import invariant from 'invariant';
4
10
  import Redis from 'ioredis';
5
11
  import nullthrows from 'nullthrows';
@@ -10,9 +16,10 @@ import GenericRedisCacher, {
10
16
  IRedis,
11
17
  IRedisTransaction,
12
18
  GenericRedisCacheContext,
19
+ RedisCacheInvalidationStrategy,
13
20
  } from '../GenericRedisCacher';
14
- import RedisTestEntity from '../testfixtures/RedisTestEntity';
15
- import { createRedisIntegrationTestEntityCompanionProvider } from '../testfixtures/createRedisIntegrationTestEntityCompanionProvider';
21
+ import RedisTestEntity, { RedisTestEntityFields } from '../__testfixtures__/RedisTestEntity';
22
+ import { createRedisIntegrationTestEntityCompanionProvider } from '../__testfixtures__/createRedisIntegrationTestEntityCompanionProvider';
16
23
 
17
24
  class BatchedRedis implements IRedis {
18
25
  private readonly mgetBatcher = new Batcher<string[], (string | null)[]>(
@@ -78,6 +85,7 @@ describe(GenericRedisCacher, () => {
78
85
  cacheKeyPrefix: 'test-',
79
86
  ttlSecondsPositive: 86400, // 1 day
80
87
  ttlSecondsNegative: 600, // 10 minutes
88
+ invalidationStrategy: RedisCacheInvalidationStrategy.CURRENT_CACHE_KEY_VERSION,
81
89
  };
82
90
  });
83
91
 
@@ -97,14 +105,14 @@ describe(GenericRedisCacher, () => {
97
105
 
98
106
  const mgetSpy = jest.spyOn(redis, 'mget');
99
107
 
100
- const genericCacher =
101
- viewerContext.entityCompanionProvider.getCompanionForEntity(RedisTestEntity)[
102
- 'tableDataCoordinator'
103
- ]['cacheAdapter']['genericCacher'];
104
- const cacheKeyMaker = genericCacher['makeCacheKey'].bind(genericCacher);
108
+ const genericCacher = viewerContext.entityCompanionProvider.getCompanionForEntity(
109
+ RedisTestEntity,
110
+ )['tableDataCoordinator']['cacheAdapter']['genericCacher'] as IEntityGenericCacher<
111
+ RedisTestEntityFields,
112
+ 'id'
113
+ >;
105
114
 
106
115
  const entity1Created = await RedisTestEntity.creator(viewerContext)
107
- .enforcing()
108
116
  .setField('name', 'blah')
109
117
  .createAsync();
110
118
 
@@ -120,11 +128,12 @@ describe(GenericRedisCacher, () => {
120
128
  createRedisIntegrationTestEntityCompanionProvider(genericRedisCacheContext),
121
129
  );
122
130
  const [entity1, entity2, entity3] = await Promise.all([
123
- RedisTestEntity.loader(viewerContext1).enforcing().loadByIDAsync(entity1Created.getID()),
124
- RedisTestEntity.loader(viewerContext2).enforcing().loadByIDAsync(entity1Created.getID()),
125
- RedisTestEntity.loader(viewerContext3)
126
- .enforcing()
127
- .loadByFieldEqualingAsync('name', entity1Created.getField('name')),
131
+ RedisTestEntity.loader(viewerContext1).loadByIDAsync(entity1Created.getID()),
132
+ RedisTestEntity.loader(viewerContext2).loadByIDAsync(entity1Created.getID()),
133
+ RedisTestEntity.loader(viewerContext3).loadByFieldEqualingAsync(
134
+ 'name',
135
+ entity1Created.getField('name'),
136
+ ),
128
137
  ]);
129
138
 
130
139
  expect(mgetSpy).toHaveBeenCalledTimes(1);
@@ -132,7 +141,10 @@ describe(GenericRedisCacher, () => {
132
141
  expect(entity1.getID()).toEqual(entity2.getID());
133
142
  expect(entity2.getID()).toEqual(nullthrows(entity3).getID());
134
143
 
135
- const cacheKeyEntity1 = cacheKeyMaker('id', entity1Created.getID());
144
+ const cacheKeyEntity1 = genericCacher.makeCacheKeyForStorage(
145
+ new SingleFieldHolder('id'),
146
+ new SingleFieldValueHolder(entity1Created.getID()),
147
+ );
136
148
  const cachedJSON = await redis.get(cacheKeyEntity1);
137
149
  const cachedValue = JSON.parse(cachedJSON!);
138
150
  expect(cachedValue).toMatchObject({
@@ -140,31 +152,38 @@ describe(GenericRedisCacher, () => {
140
152
  name: 'blah',
141
153
  });
142
154
 
143
- const cacheKeyEntity1NameField = cacheKeyMaker('name', entity1Created.getField('name'));
144
- await RedisTestEntity.loader(viewerContext)
145
- .enforcing()
146
- .loadByFieldEqualingAsync('name', entity1Created.getField('name'));
155
+ const cacheKeyEntity1NameField = genericCacher.makeCacheKeyForStorage(
156
+ new SingleFieldHolder('name'),
157
+ new SingleFieldValueHolder(entity1Created.getField('name')),
158
+ );
159
+ await RedisTestEntity.loader(viewerContext).loadByFieldEqualingAsync(
160
+ 'name',
161
+ entity1Created.getField('name'),
162
+ );
147
163
  await expect(redis.get(cacheKeyEntity1NameField)).resolves.toEqual(cachedJSON);
148
164
 
149
165
  // simulate non existent db fetch, should write negative result ('') to cache
150
166
  const nonExistentId = uuidv4();
151
- const entityNonExistentResult = await RedisTestEntity.loader(viewerContext)
152
- .withAuthorizationResults()
153
- .loadByIDAsync(nonExistentId);
167
+ const entityNonExistentResult =
168
+ await RedisTestEntity.loaderWithAuthorizationResults(viewerContext).loadByIDAsync(
169
+ nonExistentId,
170
+ );
154
171
  expect(entityNonExistentResult.ok).toBe(false);
155
- const cacheKeyNonExistent = cacheKeyMaker('id', nonExistentId);
172
+ const cacheKeyNonExistent = genericCacher.makeCacheKeyForStorage(
173
+ new SingleFieldHolder('id'),
174
+ new SingleFieldValueHolder(nonExistentId),
175
+ );
156
176
  const nonExistentCachedValue = await redis.get(cacheKeyNonExistent);
157
177
  expect(nonExistentCachedValue).toEqual('');
158
178
  // load again through entities framework to ensure it reads negative result
159
- const entityNonExistentResult2 = await RedisTestEntity.loader(viewerContext)
160
- .withAuthorizationResults()
161
- .loadByIDAsync(nonExistentId);
179
+ const entityNonExistentResult2 =
180
+ await RedisTestEntity.loaderWithAuthorizationResults(viewerContext).loadByIDAsync(
181
+ nonExistentId,
182
+ );
162
183
  expect(entityNonExistentResult2.ok).toBe(false);
163
184
 
164
185
  // invalidate from cache to ensure it invalidates correctly in both caches
165
- await RedisTestEntity.loader(viewerContext)
166
- .utils()
167
- .invalidateFieldsAsync(entity1.getAllFields());
186
+ await RedisTestEntity.loaderUtils(viewerContext).invalidateFieldsAsync(entity1.getAllFields());
168
187
  await expect(redis.get(cacheKeyEntity1)).resolves.toBeNull();
169
188
  await expect(redis.get(cacheKeyEntity1NameField)).resolves.toBeNull();
170
189
  });