@expo/entity-cache-adapter-redis 0.21.0 → 0.24.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.
@@ -1,4 +1,4 @@
1
- import { CacheLoadResult } from '@expo/entity';
1
+ import { CacheLoadResult, EntityConfiguration, IEntityGenericCacher } from '@expo/entity';
2
2
  import { Redis } from 'ioredis';
3
3
  export interface GenericRedisCacheContext {
4
4
  /**
@@ -16,11 +16,12 @@ export interface GenericRedisCacheContext {
16
16
  */
17
17
  ttlSecondsNegative: number;
18
18
  }
19
- export default class GenericRedisCacher {
19
+ export default class GenericRedisCacher<TFields> implements IEntityGenericCacher<TFields> {
20
20
  private readonly context;
21
- constructor(context: GenericRedisCacheContext);
22
- loadManyAsync(keys: readonly string[]): Promise<ReadonlyMap<string, CacheLoadResult>>;
23
- cacheManyAsync(objectMap: ReadonlyMap<string, object>): Promise<void>;
21
+ private readonly entityConfiguration;
22
+ constructor(context: GenericRedisCacheContext, entityConfiguration: EntityConfiguration<TFields>);
23
+ loadManyAsync(keys: readonly string[]): Promise<ReadonlyMap<string, CacheLoadResult<TFields>>>;
24
+ cacheManyAsync(objectMap: ReadonlyMap<string, Readonly<TFields>>): Promise<void>;
24
25
  cacheDBMissesAsync(keys: string[]): Promise<void>;
25
26
  invalidateManyAsync(keys: string[]): Promise<void>;
26
27
  }
@@ -4,13 +4,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const entity_1 = require("@expo/entity");
7
+ const RedisCommon_1 = require("./RedisCommon");
7
8
  const wrapNativeRedisCallAsync_1 = __importDefault(require("./errors/wrapNativeRedisCallAsync"));
8
9
  // Sentinel value we store in Redis to negatively cache a database miss.
9
10
  // The sentinel value is distinct from any (positively) cached value.
10
11
  const DOES_NOT_EXIST_REDIS = '';
11
12
  class GenericRedisCacher {
12
- constructor(context) {
13
+ constructor(context, entityConfiguration) {
13
14
  this.context = context;
15
+ this.entityConfiguration = entityConfiguration;
14
16
  }
15
17
  async loadManyAsync(keys) {
16
18
  if (keys.length === 0) {
@@ -29,7 +31,7 @@ class GenericRedisCacher {
29
31
  else if (redisResult) {
30
32
  results.set(key, {
31
33
  status: entity_1.CacheStatus.HIT,
32
- item: JSON.parse(redisResult),
34
+ item: (0, entity_1.transformCacheObjectToFields)(this.entityConfiguration, RedisCommon_1.redisTransformerMap, JSON.parse(redisResult)),
33
35
  });
34
36
  }
35
37
  else {
@@ -46,7 +48,7 @@ class GenericRedisCacher {
46
48
  }
47
49
  let redisTransaction = this.context.redisClient.multi();
48
50
  objectMap.forEach((object, key) => {
49
- redisTransaction = redisTransaction.set(key, JSON.stringify(object), 'EX', this.context.ttlSecondsPositive);
51
+ redisTransaction = redisTransaction.set(key, JSON.stringify((0, entity_1.transformFieldsToCacheObject)(this.entityConfiguration, RedisCommon_1.redisTransformerMap, object)), 'EX', this.context.ttlSecondsPositive);
50
52
  });
51
53
  await (0, wrapNativeRedisCallAsync_1.default)(() => redisTransaction.exec());
52
54
  }
@@ -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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"}
@@ -1,4 +1,4 @@
1
- import { EntityCacheAdapter, EntityConfiguration, CacheLoadResult, FieldTransformerMap } from '@expo/entity';
1
+ import { EntityCacheAdapter, EntityConfiguration, CacheLoadResult } from '@expo/entity';
2
2
  import { Redis } from 'ioredis';
3
3
  export interface RedisCacheAdapterContext {
4
4
  /**
@@ -35,9 +35,8 @@ export default class RedisCacheAdapter<TFields> extends EntityCacheAdapter<TFiel
35
35
  private readonly context;
36
36
  private readonly genericRedisCacher;
37
37
  constructor(entityConfiguration: EntityConfiguration<TFields>, context: RedisCacheAdapterContext);
38
- getFieldTransformerMap(): FieldTransformerMap;
39
- loadManyAsync<N extends keyof TFields>(fieldName: N, fieldValues: readonly NonNullable<TFields[N]>[]): Promise<ReadonlyMap<NonNullable<TFields[N]>, CacheLoadResult>>;
40
- cacheManyAsync<N extends keyof TFields>(fieldName: N, objectMap: ReadonlyMap<NonNullable<TFields[N]>, object>): Promise<void>;
38
+ loadManyAsync<N extends keyof TFields>(fieldName: N, fieldValues: readonly NonNullable<TFields[N]>[]): Promise<ReadonlyMap<NonNullable<TFields[N]>, CacheLoadResult<TFields>>>;
39
+ cacheManyAsync<N extends keyof TFields>(fieldName: N, objectMap: ReadonlyMap<NonNullable<TFields[N]>, Readonly<TFields>>): Promise<void>;
41
40
  cacheDBMissesAsync<N extends keyof TFields>(fieldName: N, fieldValues: readonly NonNullable<TFields[N]>[]): Promise<void>;
42
41
  invalidateManyAsync<N extends keyof TFields>(fieldName: N, fieldValues: readonly NonNullable<TFields[N]>[]): Promise<void>;
43
42
  private makeCacheKey;
@@ -6,7 +6,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const entity_1 = require("@expo/entity");
7
7
  const invariant_1 = __importDefault(require("invariant"));
8
8
  const GenericRedisCacher_1 = __importDefault(require("./GenericRedisCacher"));
9
- const RedisCommon_1 = require("./RedisCommon");
10
9
  class RedisCacheAdapter extends entity_1.EntityCacheAdapter {
11
10
  constructor(entityConfiguration, context) {
12
11
  super(entityConfiguration);
@@ -15,10 +14,7 @@ class RedisCacheAdapter extends entity_1.EntityCacheAdapter {
15
14
  redisClient: context.redisClient,
16
15
  ttlSecondsNegative: context.ttlSecondsNegative,
17
16
  ttlSecondsPositive: context.ttlSecondsPositive,
18
- });
19
- }
20
- getFieldTransformerMap() {
21
- return RedisCommon_1.redisTransformerMap;
17
+ }, entityConfiguration);
22
18
  }
23
19
  async loadManyAsync(fieldName, fieldValues) {
24
20
  const redisCacheKeyToFieldValueMapping = new Map(fieldValues.map((fieldValue) => [this.makeCacheKey(fieldName, fieldValue), fieldValue]));
@@ -1 +1 @@
1
- {"version":3,"file":"RedisCacheAdapter.js","sourceRoot":"","sources":["../src/RedisCacheAdapter.ts"],"names":[],"mappings":";;;;;AAAA,yCAMsB;AACtB,0DAAkC;AAGlC,8EAAsD;AACtD,+CAAoD;AAuCpD,MAAqB,iBAA2B,SAAQ,2BAA2B;IAEjF,YACE,mBAAiD,EAChC,OAAiC;QAElD,KAAK,CAAC,mBAAmB,CAAC,CAAC;QAFV,YAAO,GAAP,OAAO,CAA0B;QAIlD,IAAI,CAAC,kBAAkB,GAAG,IAAI,4BAAkB,CAAC;YAC/C,WAAW,EAAE,OAAO,CAAC,WAAW;YAChC,kBAAkB,EAAE,OAAO,CAAC,kBAAkB;YAC9C,kBAAkB,EAAE,OAAO,CAAC,kBAAkB;SAC/C,CAAC,CAAC;IACL,CAAC;IAEM,sBAAsB;QAC3B,OAAO,iCAAmB,CAAC;IAC7B,CAAC;IAEM,KAAK,CAAC,aAAa,CACxB,SAAY,EACZ,WAA+C;QAE/C,MAAM,gCAAgC,GAAG,IAAI,GAAG,CAC9C,WAAW,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,SAAS,EAAE,UAAU,CAAC,EAAE,UAAU,CAAC,CAAC,CACxF,CAAC;QACF,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,aAAa,CAC9D,KAAK,CAAC,IAAI,CAAC,gCAAgC,CAAC,IAAI,EAAE,CAAC,CACpD,CAAC;QAEF,OAAO,IAAA,gBAAO,EAAC,YAAY,EAAE,CAAC,aAAa,EAAE,EAAE;YAC7C,MAAM,UAAU,GAAG,gCAAgC,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;YACvE,IAAA,mBAAS,EACP,UAAU,KAAK,SAAS,EACxB,6DAA6D,EAC7D,aAAa,CACd,CAAC;YACF,OAAO,UAAU,CAAC;QACpB,CAAC,CAAC,CAAC;IACL,CAAC;IAEM,KAAK,CAAC,cAAc,CACzB,SAAY,EACZ,SAAuD;QAEvD,MAAM,IAAI,CAAC,kBAAkB,CAAC,cAAc,CAC1C,IAAA,gBAAO,EAAC,SAAS,EAAE,CAAC,UAAU,EAAE,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC,CAC7E,CAAC;IACJ,CAAC;IAEM,KAAK,CAAC,kBAAkB,CAC7B,SAAY,EACZ,WAA+C;QAE/C,MAAM,IAAI,CAAC,kBAAkB,CAAC,kBAAkB,CAC9C,WAAW,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC,CAC1E,CAAC;IACJ,CAAC;IAEM,KAAK,CAAC,mBAAmB,CAC9B,SAAY,EACZ,WAA+C;QAE/C,MAAM,IAAI,CAAC,kBAAkB,CAAC,mBAAmB,CAC/C,WAAW,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC,CAC1E,CAAC;IACJ,CAAC;IAEO,YAAY,CAClB,SAAY,EACZ,UAAmC;QAEnC,MAAM,UAAU,GAAG,IAAI,CAAC,mBAAmB,CAAC,0BAA0B,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACtF,IAAA,mBAAS,EAAC,UAAU,EAAE,sCAAsC,SAAS,EAAE,CAAC,CAAC;QACzE,OAAO,IAAI,CAAC,OAAO,CAAC,SAAS,CAC3B,IAAI,CAAC,OAAO,CAAC,cAAc,EAC3B,IAAI,CAAC,mBAAmB,CAAC,SAAS,EAClC,MAAM,IAAI,CAAC,mBAAmB,CAAC,eAAe,EAAE,EAChD,UAAU,EACV,MAAM,CAAC,UAAU,CAAC,CACnB,CAAC;IACJ,CAAC;CACF;AAlFD,oCAkFC"}
1
+ {"version":3,"file":"RedisCacheAdapter.js","sourceRoot":"","sources":["../src/RedisCacheAdapter.ts"],"names":[],"mappings":";;;;;AAAA,yCAAiG;AACjG,0DAAkC;AAGlC,8EAAsD;AAuCtD,MAAqB,iBAA2B,SAAQ,2BAA2B;IAEjF,YACE,mBAAiD,EAChC,OAAiC;QAElD,KAAK,CAAC,mBAAmB,CAAC,CAAC;QAFV,YAAO,GAAP,OAAO,CAA0B;QAIlD,IAAI,CAAC,kBAAkB,GAAG,IAAI,4BAAkB,CAC9C;YACE,WAAW,EAAE,OAAO,CAAC,WAAW;YAChC,kBAAkB,EAAE,OAAO,CAAC,kBAAkB;YAC9C,kBAAkB,EAAE,OAAO,CAAC,kBAAkB;SAC/C,EACD,mBAAmB,CACpB,CAAC;IACJ,CAAC;IAEM,KAAK,CAAC,aAAa,CACxB,SAAY,EACZ,WAA+C;QAE/C,MAAM,gCAAgC,GAAG,IAAI,GAAG,CAC9C,WAAW,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,SAAS,EAAE,UAAU,CAAC,EAAE,UAAU,CAAC,CAAC,CACxF,CAAC;QACF,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,aAAa,CAC9D,KAAK,CAAC,IAAI,CAAC,gCAAgC,CAAC,IAAI,EAAE,CAAC,CACpD,CAAC;QAEF,OAAO,IAAA,gBAAO,EAAC,YAAY,EAAE,CAAC,aAAa,EAAE,EAAE;YAC7C,MAAM,UAAU,GAAG,gCAAgC,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;YACvE,IAAA,mBAAS,EACP,UAAU,KAAK,SAAS,EACxB,6DAA6D,EAC7D,aAAa,CACd,CAAC;YACF,OAAO,UAAU,CAAC;QACpB,CAAC,CAAC,CAAC;IACL,CAAC;IAEM,KAAK,CAAC,cAAc,CACzB,SAAY,EACZ,SAAkE;QAElE,MAAM,IAAI,CAAC,kBAAkB,CAAC,cAAc,CAC1C,IAAA,gBAAO,EAAC,SAAS,EAAE,CAAC,UAAU,EAAE,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC,CAC7E,CAAC;IACJ,CAAC;IAEM,KAAK,CAAC,kBAAkB,CAC7B,SAAY,EACZ,WAA+C;QAE/C,MAAM,IAAI,CAAC,kBAAkB,CAAC,kBAAkB,CAC9C,WAAW,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC,CAC1E,CAAC;IACJ,CAAC;IAEM,KAAK,CAAC,mBAAmB,CAC9B,SAAY,EACZ,WAA+C;QAE/C,MAAM,IAAI,CAAC,kBAAkB,CAAC,mBAAmB,CAC/C,WAAW,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC,CAC1E,CAAC;IACJ,CAAC;IAEO,YAAY,CAClB,SAAY,EACZ,UAAmC;QAEnC,MAAM,UAAU,GAAG,IAAI,CAAC,mBAAmB,CAAC,0BAA0B,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACtF,IAAA,mBAAS,EAAC,UAAU,EAAE,sCAAsC,SAAS,EAAE,CAAC,CAAC;QACzE,OAAO,IAAI,CAAC,OAAO,CAAC,SAAS,CAC3B,IAAI,CAAC,OAAO,CAAC,cAAc,EAC3B,IAAI,CAAC,mBAAmB,CAAC,SAAS,EAClC,MAAM,IAAI,CAAC,mBAAmB,CAAC,eAAe,EAAE,EAChD,UAAU,EACV,MAAM,CAAC,UAAU,CAAC,CACnB,CAAC;IACJ,CAAC;CACF;AAjFD,oCAiFC"}
@@ -0,0 +1,121 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5
+ }) : (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ o[k2] = m[k];
8
+ }));
9
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
10
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
11
+ }) : function(o, v) {
12
+ o["default"] = v;
13
+ });
14
+ var __importStar = (this && this.__importStar) || function (mod) {
15
+ if (mod && mod.__esModule) return mod;
16
+ var result = {};
17
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18
+ __setModuleDefault(result, mod);
19
+ return result;
20
+ };
21
+ var __importDefault = (this && this.__importDefault) || function (mod) {
22
+ return (mod && mod.__esModule) ? mod : { "default": mod };
23
+ };
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ const entity_1 = require("@expo/entity");
26
+ const ioredis_1 = __importDefault(require("ioredis"));
27
+ const url_1 = require("url");
28
+ const GenericRedisCacher_1 = __importDefault(require("../GenericRedisCacher"));
29
+ const RedisTestEntity_1 = __importStar(require("../testfixtures/RedisTestEntity"));
30
+ const createRedisIntegrationTestEntityCompanionProvider_1 = require("../testfixtures/createRedisIntegrationTestEntityCompanionProvider");
31
+ class TestViewerContext extends entity_1.ViewerContext {
32
+ }
33
+ describe(GenericRedisCacher_1.default, () => {
34
+ let redisCacheAdapterContext;
35
+ beforeAll(() => {
36
+ redisCacheAdapterContext = {
37
+ redisClient: new ioredis_1.default(new url_1.URL(process.env['REDIS_URL']).toString()),
38
+ makeKeyFn(...parts) {
39
+ const delimiter = ':';
40
+ const escapedParts = parts.map((part) => part.replace('\\', '\\\\').replace(delimiter, `\\${delimiter}`));
41
+ return escapedParts.join(delimiter);
42
+ },
43
+ cacheKeyPrefix: 'test-',
44
+ cacheKeyVersion: 1,
45
+ ttlSecondsPositive: 86400,
46
+ ttlSecondsNegative: 600, // 10 minutes
47
+ };
48
+ });
49
+ beforeEach(async () => {
50
+ await redisCacheAdapterContext.redisClient.flushdb();
51
+ });
52
+ afterAll(async () => {
53
+ redisCacheAdapterContext.redisClient.disconnect();
54
+ });
55
+ it('has correct caching and loading behavior', async () => {
56
+ const viewerContext = new TestViewerContext((0, createRedisIntegrationTestEntityCompanionProvider_1.createRedisIntegrationTestEntityCompanionProvider)(redisCacheAdapterContext));
57
+ const genericRedisCacher = new GenericRedisCacher_1.default({
58
+ redisClient: redisCacheAdapterContext.redisClient,
59
+ ttlSecondsNegative: redisCacheAdapterContext.ttlSecondsNegative,
60
+ ttlSecondsPositive: redisCacheAdapterContext.ttlSecondsPositive,
61
+ }, RedisTestEntity_1.redisTestEntityConfiguration);
62
+ const date = new Date();
63
+ const entity1Created = await RedisTestEntity_1.default.creator(viewerContext)
64
+ .setField('name', 'blah')
65
+ .setField('dateField', date)
66
+ .enforceCreateAsync();
67
+ const testKey = `test-id-key-${entity1Created.getID()}`;
68
+ const objectMap = new Map([
69
+ [testKey, entity1Created.getAllFields()],
70
+ ]);
71
+ await genericRedisCacher.cacheManyAsync(objectMap);
72
+ const cachedJSON = await redisCacheAdapterContext.redisClient.get(testKey);
73
+ const cachedValue = JSON.parse(cachedJSON);
74
+ expect(cachedValue).toMatchObject({
75
+ id: entity1Created.getID(),
76
+ dateField: date.toISOString(),
77
+ });
78
+ const loadedObjectMap = await genericRedisCacher.loadManyAsync([testKey]);
79
+ const cacheLoadResult = loadedObjectMap.get(testKey);
80
+ expect(cacheLoadResult).toMatchObject({
81
+ status: entity_1.CacheStatus.HIT,
82
+ item: entity1Created.getAllFields(),
83
+ });
84
+ expect(loadedObjectMap.size).toBe(1);
85
+ });
86
+ it('has correct negative caching behaviour', async () => {
87
+ const genericRedisCacher = new GenericRedisCacher_1.default({
88
+ redisClient: redisCacheAdapterContext.redisClient,
89
+ ttlSecondsNegative: redisCacheAdapterContext.ttlSecondsNegative,
90
+ ttlSecondsPositive: redisCacheAdapterContext.ttlSecondsPositive,
91
+ }, RedisTestEntity_1.redisTestEntityConfiguration);
92
+ const testKey = `test-id-key-non-existent-id`;
93
+ await genericRedisCacher.cacheDBMissesAsync([testKey]);
94
+ const loadedObjectMap = await genericRedisCacher.loadManyAsync([testKey]);
95
+ const cacheLoadResult = loadedObjectMap.get(testKey);
96
+ expect(cacheLoadResult.status).toBe(entity_1.CacheStatus.NEGATIVE);
97
+ });
98
+ it('has correct invalidation behaviour', async () => {
99
+ const viewerContext = new TestViewerContext((0, createRedisIntegrationTestEntityCompanionProvider_1.createRedisIntegrationTestEntityCompanionProvider)(redisCacheAdapterContext));
100
+ const genericRedisCacher = new GenericRedisCacher_1.default({
101
+ redisClient: redisCacheAdapterContext.redisClient,
102
+ ttlSecondsNegative: redisCacheAdapterContext.ttlSecondsNegative,
103
+ ttlSecondsPositive: redisCacheAdapterContext.ttlSecondsPositive,
104
+ }, RedisTestEntity_1.redisTestEntityConfiguration);
105
+ const date = new Date();
106
+ const entity1Created = await RedisTestEntity_1.default.creator(viewerContext)
107
+ .setField('name', 'blah')
108
+ .setField('dateField', date)
109
+ .enforceCreateAsync();
110
+ const testKey = `test-id-key-${entity1Created.getID()}`;
111
+ const objectMap = new Map([
112
+ [testKey, entity1Created.getAllFields()],
113
+ ]);
114
+ await genericRedisCacher.cacheManyAsync(objectMap);
115
+ await genericRedisCacher.invalidateManyAsync([testKey]);
116
+ const loadedObjectMap = await genericRedisCacher.loadManyAsync([testKey]);
117
+ const cacheLoadResult = loadedObjectMap.get(testKey);
118
+ expect(cacheLoadResult.status).toBe(entity_1.CacheStatus.MISS);
119
+ });
120
+ });
121
+ //# sourceMappingURL=GenericRedisCacher-integration-test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GenericRedisCacher-integration-test.js","sourceRoot":"","sources":["../../src/__integration-tests__/GenericRedisCacher-integration-test.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@expo/entity-cache-adapter-redis",
3
- "version": "0.21.0",
3
+ "version": "0.24.0",
4
4
  "description": "Redis cache adapter for @expo/entity",
5
5
  "files": [
6
6
  "build",
@@ -33,6 +33,6 @@
33
33
  "ioredis": "^4.27.3"
34
34
  },
35
35
  "devDependencies": {
36
- "@expo/entity": "^0.21.0"
36
+ "@expo/entity": "^0.24.0"
37
37
  }
38
38
  }
@@ -1,6 +1,14 @@
1
- import { CacheLoadResult, CacheStatus } from '@expo/entity';
1
+ import {
2
+ CacheLoadResult,
3
+ CacheStatus,
4
+ EntityConfiguration,
5
+ transformCacheObjectToFields,
6
+ transformFieldsToCacheObject,
7
+ IEntityGenericCacher,
8
+ } from '@expo/entity';
2
9
  import { Redis } from 'ioredis';
3
10
 
11
+ import { redisTransformerMap } from './RedisCommon';
4
12
  import wrapNativeRedisCallAsync from './errors/wrapNativeRedisCallAsync';
5
13
 
6
14
  // Sentinel value we store in Redis to negatively cache a database miss.
@@ -26,12 +34,15 @@ export interface GenericRedisCacheContext {
26
34
  ttlSecondsNegative: number;
27
35
  }
28
36
 
29
- export default class GenericRedisCacher {
30
- constructor(private readonly context: GenericRedisCacheContext) {}
37
+ export default class GenericRedisCacher<TFields> implements IEntityGenericCacher<TFields> {
38
+ constructor(
39
+ private readonly context: GenericRedisCacheContext,
40
+ private readonly entityConfiguration: EntityConfiguration<TFields>
41
+ ) {}
31
42
 
32
43
  public async loadManyAsync(
33
44
  keys: readonly string[]
34
- ): Promise<ReadonlyMap<string, CacheLoadResult>> {
45
+ ): Promise<ReadonlyMap<string, CacheLoadResult<TFields>>> {
35
46
  if (keys.length === 0) {
36
47
  return new Map();
37
48
  }
@@ -40,7 +51,7 @@ export default class GenericRedisCacher {
40
51
  this.context.redisClient.mget(...keys)
41
52
  );
42
53
 
43
- const results = new Map<string, CacheLoadResult>();
54
+ const results = new Map<string, CacheLoadResult<TFields>>();
44
55
  for (let i = 0; i < keys.length; i++) {
45
56
  const key = keys[i]!;
46
57
  const redisResult = redisResults[i];
@@ -52,7 +63,11 @@ export default class GenericRedisCacher {
52
63
  } else if (redisResult) {
53
64
  results.set(key, {
54
65
  status: CacheStatus.HIT,
55
- item: JSON.parse(redisResult),
66
+ item: transformCacheObjectToFields(
67
+ this.entityConfiguration,
68
+ redisTransformerMap,
69
+ JSON.parse(redisResult)
70
+ ),
56
71
  });
57
72
  } else {
58
73
  results.set(key, {
@@ -63,7 +78,7 @@ export default class GenericRedisCacher {
63
78
  return results;
64
79
  }
65
80
 
66
- public async cacheManyAsync(objectMap: ReadonlyMap<string, object>): Promise<void> {
81
+ public async cacheManyAsync(objectMap: ReadonlyMap<string, Readonly<TFields>>): Promise<void> {
67
82
  if (objectMap.size === 0) {
68
83
  return;
69
84
  }
@@ -72,7 +87,9 @@ export default class GenericRedisCacher {
72
87
  objectMap.forEach((object, key) => {
73
88
  redisTransaction = redisTransaction.set(
74
89
  key,
75
- JSON.stringify(object),
90
+ JSON.stringify(
91
+ transformFieldsToCacheObject(this.entityConfiguration, redisTransformerMap, object)
92
+ ),
76
93
  'EX',
77
94
  this.context.ttlSecondsPositive
78
95
  );
@@ -1,15 +1,8 @@
1
- import {
2
- EntityCacheAdapter,
3
- EntityConfiguration,
4
- CacheLoadResult,
5
- FieldTransformerMap,
6
- mapKeys,
7
- } from '@expo/entity';
1
+ import { EntityCacheAdapter, EntityConfiguration, CacheLoadResult, mapKeys } from '@expo/entity';
8
2
  import invariant from 'invariant';
9
3
  import { Redis } from 'ioredis';
10
4
 
11
5
  import GenericRedisCacher from './GenericRedisCacher';
12
- import { redisTransformerMap } from './RedisCommon';
13
6
 
14
7
  export interface RedisCacheAdapterContext {
15
8
  /**
@@ -49,28 +42,27 @@ export interface RedisCacheAdapterContext {
49
42
  }
50
43
 
51
44
  export default class RedisCacheAdapter<TFields> extends EntityCacheAdapter<TFields> {
52
- private readonly genericRedisCacher: GenericRedisCacher;
45
+ private readonly genericRedisCacher: GenericRedisCacher<TFields>;
53
46
  constructor(
54
47
  entityConfiguration: EntityConfiguration<TFields>,
55
48
  private readonly context: RedisCacheAdapterContext
56
49
  ) {
57
50
  super(entityConfiguration);
58
51
 
59
- this.genericRedisCacher = new GenericRedisCacher({
60
- redisClient: context.redisClient,
61
- ttlSecondsNegative: context.ttlSecondsNegative,
62
- ttlSecondsPositive: context.ttlSecondsPositive,
63
- });
64
- }
65
-
66
- public getFieldTransformerMap(): FieldTransformerMap {
67
- return redisTransformerMap;
52
+ this.genericRedisCacher = new GenericRedisCacher(
53
+ {
54
+ redisClient: context.redisClient,
55
+ ttlSecondsNegative: context.ttlSecondsNegative,
56
+ ttlSecondsPositive: context.ttlSecondsPositive,
57
+ },
58
+ entityConfiguration
59
+ );
68
60
  }
69
61
 
70
62
  public async loadManyAsync<N extends keyof TFields>(
71
63
  fieldName: N,
72
64
  fieldValues: readonly NonNullable<TFields[N]>[]
73
- ): Promise<ReadonlyMap<NonNullable<TFields[N]>, CacheLoadResult>> {
65
+ ): Promise<ReadonlyMap<NonNullable<TFields[N]>, CacheLoadResult<TFields>>> {
74
66
  const redisCacheKeyToFieldValueMapping = new Map(
75
67
  fieldValues.map((fieldValue) => [this.makeCacheKey(fieldName, fieldValue), fieldValue])
76
68
  );
@@ -91,7 +83,7 @@ export default class RedisCacheAdapter<TFields> extends EntityCacheAdapter<TFiel
91
83
 
92
84
  public async cacheManyAsync<N extends keyof TFields>(
93
85
  fieldName: N,
94
- objectMap: ReadonlyMap<NonNullable<TFields[N]>, object>
86
+ objectMap: ReadonlyMap<NonNullable<TFields[N]>, Readonly<TFields>>
95
87
  ): Promise<void> {
96
88
  await this.genericRedisCacher.cacheManyAsync(
97
89
  mapKeys(objectMap, (fieldValue) => this.makeCacheKey(fieldName, fieldValue))
@@ -0,0 +1,125 @@
1
+ import { CacheStatus, ViewerContext } from '@expo/entity';
2
+ import Redis from 'ioredis';
3
+ import { URL } from 'url';
4
+
5
+ import GenericRedisCacher from '../GenericRedisCacher';
6
+ import { RedisCacheAdapterContext } from '../RedisCacheAdapter';
7
+ import RedisTestEntity, {
8
+ redisTestEntityConfiguration,
9
+ RedisTestEntityFields,
10
+ } from '../testfixtures/RedisTestEntity';
11
+ import { createRedisIntegrationTestEntityCompanionProvider } from '../testfixtures/createRedisIntegrationTestEntityCompanionProvider';
12
+
13
+ class TestViewerContext extends ViewerContext {}
14
+
15
+ describe(GenericRedisCacher, () => {
16
+ let redisCacheAdapterContext: RedisCacheAdapterContext;
17
+
18
+ beforeAll(() => {
19
+ redisCacheAdapterContext = {
20
+ redisClient: new Redis(new URL(process.env['REDIS_URL']!).toString()),
21
+ makeKeyFn(...parts: string[]): string {
22
+ const delimiter = ':';
23
+ const escapedParts = parts.map((part) =>
24
+ part.replace('\\', '\\\\').replace(delimiter, `\\${delimiter}`)
25
+ );
26
+ return escapedParts.join(delimiter);
27
+ },
28
+ cacheKeyPrefix: 'test-',
29
+ cacheKeyVersion: 1,
30
+ ttlSecondsPositive: 86400, // 1 day
31
+ ttlSecondsNegative: 600, // 10 minutes
32
+ };
33
+ });
34
+
35
+ beforeEach(async () => {
36
+ await redisCacheAdapterContext.redisClient.flushdb();
37
+ });
38
+ afterAll(async () => {
39
+ redisCacheAdapterContext.redisClient.disconnect();
40
+ });
41
+
42
+ it('has correct caching and loading behavior', async () => {
43
+ const viewerContext = new TestViewerContext(
44
+ createRedisIntegrationTestEntityCompanionProvider(redisCacheAdapterContext)
45
+ );
46
+ const genericRedisCacher = new GenericRedisCacher(
47
+ {
48
+ redisClient: redisCacheAdapterContext.redisClient,
49
+ ttlSecondsNegative: redisCacheAdapterContext.ttlSecondsNegative,
50
+ ttlSecondsPositive: redisCacheAdapterContext.ttlSecondsPositive,
51
+ },
52
+ redisTestEntityConfiguration
53
+ );
54
+ const date = new Date();
55
+ const entity1Created = await RedisTestEntity.creator(viewerContext)
56
+ .setField('name', 'blah')
57
+ .setField('dateField', date)
58
+ .enforceCreateAsync();
59
+ const testKey = `test-id-key-${entity1Created.getID()}`;
60
+ const objectMap = new Map<string, Readonly<RedisTestEntityFields>>([
61
+ [testKey, entity1Created.getAllFields()],
62
+ ]);
63
+ await genericRedisCacher.cacheManyAsync(objectMap);
64
+
65
+ const cachedJSON = await redisCacheAdapterContext.redisClient.get(testKey);
66
+ const cachedValue = JSON.parse(cachedJSON!);
67
+ expect(cachedValue).toMatchObject({
68
+ id: entity1Created.getID(),
69
+ dateField: date.toISOString(),
70
+ });
71
+
72
+ const loadedObjectMap = await genericRedisCacher.loadManyAsync([testKey]);
73
+ const cacheLoadResult = loadedObjectMap.get(testKey)!;
74
+ expect(cacheLoadResult).toMatchObject({
75
+ status: CacheStatus.HIT,
76
+ item: entity1Created.getAllFields(),
77
+ });
78
+ expect(loadedObjectMap.size).toBe(1);
79
+ });
80
+ it('has correct negative caching behaviour', async () => {
81
+ const genericRedisCacher = new GenericRedisCacher(
82
+ {
83
+ redisClient: redisCacheAdapterContext.redisClient,
84
+ ttlSecondsNegative: redisCacheAdapterContext.ttlSecondsNegative,
85
+ ttlSecondsPositive: redisCacheAdapterContext.ttlSecondsPositive,
86
+ },
87
+ redisTestEntityConfiguration
88
+ );
89
+
90
+ const testKey = `test-id-key-non-existent-id`;
91
+ await genericRedisCacher.cacheDBMissesAsync([testKey]);
92
+ const loadedObjectMap = await genericRedisCacher.loadManyAsync([testKey]);
93
+ const cacheLoadResult = loadedObjectMap.get(testKey)!;
94
+ expect(cacheLoadResult.status).toBe(CacheStatus.NEGATIVE);
95
+ });
96
+ it('has correct invalidation behaviour', async () => {
97
+ const viewerContext = new TestViewerContext(
98
+ createRedisIntegrationTestEntityCompanionProvider(redisCacheAdapterContext)
99
+ );
100
+ const genericRedisCacher = new GenericRedisCacher(
101
+ {
102
+ redisClient: redisCacheAdapterContext.redisClient,
103
+ ttlSecondsNegative: redisCacheAdapterContext.ttlSecondsNegative,
104
+ ttlSecondsPositive: redisCacheAdapterContext.ttlSecondsPositive,
105
+ },
106
+ redisTestEntityConfiguration
107
+ );
108
+ const date = new Date();
109
+ const entity1Created = await RedisTestEntity.creator(viewerContext)
110
+ .setField('name', 'blah')
111
+ .setField('dateField', date)
112
+ .enforceCreateAsync();
113
+ const testKey = `test-id-key-${entity1Created.getID()}`;
114
+ const objectMap = new Map<string, Readonly<RedisTestEntityFields>>([
115
+ [testKey, entity1Created.getAllFields()],
116
+ ]);
117
+ await genericRedisCacher.cacheManyAsync(objectMap);
118
+
119
+ await genericRedisCacher.invalidateManyAsync([testKey]);
120
+
121
+ const loadedObjectMap = await genericRedisCacher.loadManyAsync([testKey]);
122
+ const cacheLoadResult = loadedObjectMap.get(testKey)!;
123
+ expect(cacheLoadResult.status).toBe(CacheStatus.MISS);
124
+ });
125
+ });