@expo/entity-cache-adapter-redis 0.41.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 +29 -11
  17. package/src/__integration-tests__/GenericRedisCacher-full-integration-test.ts +83 -18
  18. package/src/__integration-tests__/GenericRedisCacher-integration-test.ts +7 -3
  19. package/src/__integration-tests__/errors-test.ts +7 -3
  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 -122
  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 -93
  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 -122
  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
@@ -2,12 +2,15 @@ import { CacheStatus, ViewerContext } from '@expo/entity';
2
2
  import Redis from 'ioredis';
3
3
  import { URL } from 'url';
4
4
 
5
- import GenericRedisCacher, { GenericRedisCacheContext } from '../GenericRedisCacher';
5
+ import GenericRedisCacher, {
6
+ GenericRedisCacheContext,
7
+ RedisCacheInvalidationStrategy,
8
+ } from '../GenericRedisCacher';
6
9
  import RedisTestEntity, {
7
10
  redisTestEntityConfiguration,
8
11
  RedisTestEntityFields,
9
- } from '../testfixtures/RedisTestEntity';
10
- import { createRedisIntegrationTestEntityCompanionProvider } from '../testfixtures/createRedisIntegrationTestEntityCompanionProvider';
12
+ } from '../__testfixtures__/RedisTestEntity';
13
+ import { createRedisIntegrationTestEntityCompanionProvider } from '../__testfixtures__/createRedisIntegrationTestEntityCompanionProvider';
11
14
 
12
15
  class TestViewerContext extends ViewerContext {}
13
16
 
@@ -27,6 +30,7 @@ describe(GenericRedisCacher, () => {
27
30
  cacheKeyPrefix: 'test-',
28
31
  ttlSecondsPositive: 86400, // 1 day
29
32
  ttlSecondsNegative: 600, // 10 minutes
33
+ invalidationStrategy: RedisCacheInvalidationStrategy.CURRENT_CACHE_KEY_VERSION,
30
34
  };
31
35
  });
32
36
 
@@ -2,9 +2,12 @@ import { EntityCacheAdapterTransientError, ViewerContext } from '@expo/entity';
2
2
  import Redis from 'ioredis';
3
3
  import { URL } from 'url';
4
4
 
5
- import GenericRedisCacher, { GenericRedisCacheContext } from '../GenericRedisCacher';
6
- import RedisTestEntity from '../testfixtures/RedisTestEntity';
7
- import { createRedisIntegrationTestEntityCompanionProvider } from '../testfixtures/createRedisIntegrationTestEntityCompanionProvider';
5
+ import GenericRedisCacher, {
6
+ GenericRedisCacheContext,
7
+ RedisCacheInvalidationStrategy,
8
+ } from '../GenericRedisCacher';
9
+ import RedisTestEntity from '../__testfixtures__/RedisTestEntity';
10
+ import { createRedisIntegrationTestEntityCompanionProvider } from '../__testfixtures__/createRedisIntegrationTestEntityCompanionProvider';
8
11
 
9
12
  class TestViewerContext extends ViewerContext {}
10
13
 
@@ -25,6 +28,7 @@ describe(GenericRedisCacher, () => {
25
28
  cacheKeyPrefix: 'test-',
26
29
  ttlSecondsPositive: 86400, // 1 day
27
30
  ttlSecondsNegative: 600, // 10 minutes
31
+ invalidationStrategy: RedisCacheInvalidationStrategy.CURRENT_CACHE_KEY_VERSION,
28
32
  };
29
33
  });
30
34
 
@@ -2,12 +2,12 @@ import {
2
2
  AlwaysAllowPrivacyPolicyRule,
3
3
  EntityPrivacyPolicy,
4
4
  ViewerContext,
5
- UUIDField,
6
5
  DateField,
7
6
  StringField,
8
7
  EntityConfiguration,
9
8
  EntityCompanionDefinition,
10
9
  Entity,
10
+ UUIDField,
11
11
  } from '@expo/entity';
12
12
 
13
13
  export type RedisTestEntityFields = {
@@ -16,10 +16,10 @@ export type RedisTestEntityFields = {
16
16
  dateField: Date | null;
17
17
  };
18
18
 
19
- export default class RedisTestEntity extends Entity<RedisTestEntityFields, string, ViewerContext> {
19
+ export default class RedisTestEntity extends Entity<RedisTestEntityFields, 'id', ViewerContext> {
20
20
  static defineCompanionDefinition(): EntityCompanionDefinition<
21
21
  RedisTestEntityFields,
22
- string,
22
+ 'id',
23
23
  ViewerContext,
24
24
  RedisTestEntity,
25
25
  RedisTestEntityPrivacyPolicy
@@ -34,45 +34,25 @@ export default class RedisTestEntity extends Entity<RedisTestEntityFields, strin
34
34
 
35
35
  export class RedisTestEntityPrivacyPolicy extends EntityPrivacyPolicy<
36
36
  RedisTestEntityFields,
37
- string,
37
+ 'id',
38
38
  ViewerContext,
39
39
  RedisTestEntity
40
40
  > {
41
41
  protected override readonly createRules = [
42
- new AlwaysAllowPrivacyPolicyRule<
43
- RedisTestEntityFields,
44
- string,
45
- ViewerContext,
46
- RedisTestEntity
47
- >(),
42
+ new AlwaysAllowPrivacyPolicyRule<RedisTestEntityFields, 'id', ViewerContext, RedisTestEntity>(),
48
43
  ];
49
44
  protected override readonly readRules = [
50
- new AlwaysAllowPrivacyPolicyRule<
51
- RedisTestEntityFields,
52
- string,
53
- ViewerContext,
54
- RedisTestEntity
55
- >(),
45
+ new AlwaysAllowPrivacyPolicyRule<RedisTestEntityFields, 'id', ViewerContext, RedisTestEntity>(),
56
46
  ];
57
47
  protected override readonly updateRules = [
58
- new AlwaysAllowPrivacyPolicyRule<
59
- RedisTestEntityFields,
60
- string,
61
- ViewerContext,
62
- RedisTestEntity
63
- >(),
48
+ new AlwaysAllowPrivacyPolicyRule<RedisTestEntityFields, 'id', ViewerContext, RedisTestEntity>(),
64
49
  ];
65
50
  protected override readonly deleteRules = [
66
- new AlwaysAllowPrivacyPolicyRule<
67
- RedisTestEntityFields,
68
- string,
69
- ViewerContext,
70
- RedisTestEntity
71
- >(),
51
+ new AlwaysAllowPrivacyPolicyRule<RedisTestEntityFields, 'id', ViewerContext, RedisTestEntity>(),
72
52
  ];
73
53
  }
74
54
 
75
- export const redisTestEntityConfiguration = new EntityConfiguration<RedisTestEntityFields>({
55
+ export const redisTestEntityConfiguration = new EntityConfiguration<RedisTestEntityFields, 'id'>({
76
56
  idField: 'id',
77
57
  tableName: 'redis_test_entities',
78
58
  schema: {
@@ -90,4 +70,8 @@ export const redisTestEntityConfiguration = new EntityConfiguration<RedisTestEnt
90
70
  },
91
71
  databaseAdapterFlavor: 'postgres',
92
72
  cacheAdapterFlavor: 'redis',
73
+ compositeFieldDefinitions: [
74
+ { compositeField: ['id', 'name'], cache: true },
75
+ { compositeField: ['id', 'dateField'], cache: true },
76
+ ],
93
77
  });
@@ -2,9 +2,8 @@ import {
2
2
  NoOpEntityMetricsAdapter,
3
3
  IEntityMetricsAdapter,
4
4
  EntityCompanionProvider,
5
- StubQueryContextProvider,
6
- StubDatabaseAdapterProvider,
7
5
  } from '@expo/entity';
6
+ import { StubDatabaseAdapterProvider, StubQueryContextProvider } from '@expo/entity-testing-utils';
8
7
 
9
8
  import { GenericRedisCacheContext } from '../GenericRedisCacher';
10
9
  import RedisCacheAdapterProvider from '../RedisCacheAdapterProvider';
@@ -1,16 +1,23 @@
1
- import { CacheStatus, UUIDField, EntityConfiguration } from '@expo/entity';
1
+ import {
2
+ CacheStatus,
3
+ EntityConfiguration,
4
+ SingleFieldHolder,
5
+ SingleFieldValueHolder,
6
+ UUIDField,
7
+ } from '@expo/entity';
2
8
  import { Redis, Pipeline } from 'ioredis';
3
9
  import { mock, when, instance, anything, verify } from 'ts-mockito';
4
10
 
5
- import GenericRedisCacher from '../GenericRedisCacher';
11
+ import GenericRedisCacher, { RedisCacheInvalidationStrategy } from '../GenericRedisCacher';
6
12
 
7
13
  type BlahFields = {
8
14
  id: string;
9
15
  };
10
16
 
11
- const entityConfiguration = new EntityConfiguration<BlahFields>({
17
+ const entityConfiguration = new EntityConfiguration<BlahFields, 'id'>({
12
18
  idField: 'id',
13
19
  tableName: 'blah',
20
+ cacheKeyVersion: 2,
14
21
  schema: {
15
22
  id: new UUIDField({ columnName: 'id', cache: true }),
16
23
  },
@@ -37,13 +44,23 @@ describe(GenericRedisCacher, () => {
37
44
  cacheKeyPrefix: 'hello-',
38
45
  ttlSecondsPositive: 1,
39
46
  ttlSecondsNegative: 2,
47
+ invalidationStrategy: RedisCacheInvalidationStrategy.CURRENT_CACHE_KEY_VERSION,
40
48
  },
41
49
  entityConfiguration,
42
50
  );
43
51
 
44
- const cacheKeyWat = genericCacher['makeCacheKey']('id', 'wat');
45
- const cacheKeyWho = genericCacher['makeCacheKey']('id', 'who');
46
- const cacheKeyWhy = genericCacher['makeCacheKey']('id', 'why');
52
+ const cacheKeyWat = genericCacher['makeCacheKeyForStorage'](
53
+ new SingleFieldHolder('id'),
54
+ new SingleFieldValueHolder('wat'),
55
+ );
56
+ const cacheKeyWho = genericCacher['makeCacheKeyForStorage'](
57
+ new SingleFieldHolder('id'),
58
+ new SingleFieldValueHolder('who'),
59
+ );
60
+ const cacheKeyWhy = genericCacher['makeCacheKeyForStorage'](
61
+ new SingleFieldHolder('id'),
62
+ new SingleFieldValueHolder('why'),
63
+ );
47
64
 
48
65
  redisResults.set(cacheKeyWat, JSON.stringify({ id: 'wat' }));
49
66
  redisResults.set(cacheKeyWho, '');
@@ -67,6 +84,7 @@ describe(GenericRedisCacher, () => {
67
84
  cacheKeyPrefix: 'hello-',
68
85
  ttlSecondsPositive: 1,
69
86
  ttlSecondsNegative: 2,
87
+ invalidationStrategy: RedisCacheInvalidationStrategy.CURRENT_CACHE_KEY_VERSION,
70
88
  },
71
89
  entityConfiguration,
72
90
  );
@@ -99,11 +117,15 @@ describe(GenericRedisCacher, () => {
99
117
  cacheKeyPrefix: 'hello-',
100
118
  ttlSecondsPositive: 1,
101
119
  ttlSecondsNegative: 2,
120
+ invalidationStrategy: RedisCacheInvalidationStrategy.CURRENT_CACHE_KEY_VERSION,
102
121
  },
103
122
  entityConfiguration,
104
123
  );
105
124
 
106
- const cacheKey = genericCacher['makeCacheKey']('id', 'wat');
125
+ const cacheKey = genericCacher['makeCacheKeyForStorage'](
126
+ new SingleFieldHolder('id'),
127
+ new SingleFieldValueHolder('wat'),
128
+ );
107
129
 
108
130
  await genericCacher.cacheManyAsync(new Map([[cacheKey, { id: 'wat' }]]));
109
131
 
@@ -138,11 +160,15 @@ describe(GenericRedisCacher, () => {
138
160
  cacheKeyPrefix: 'hello-',
139
161
  ttlSecondsPositive: 1,
140
162
  ttlSecondsNegative: 2,
163
+ invalidationStrategy: RedisCacheInvalidationStrategy.CURRENT_CACHE_KEY_VERSION,
141
164
  },
142
165
  entityConfiguration,
143
166
  );
144
167
 
145
- const cacheKey = genericCacher['makeCacheKey']('id', 'wat');
168
+ const cacheKey = genericCacher['makeCacheKeyForStorage'](
169
+ new SingleFieldHolder('id'),
170
+ new SingleFieldValueHolder('wat'),
171
+ );
146
172
 
147
173
  await genericCacher.cacheDBMissesAsync([cacheKey]);
148
174
 
@@ -155,7 +181,7 @@ describe(GenericRedisCacher, () => {
155
181
  });
156
182
 
157
183
  describe('invalidateManyAsync', () => {
158
- it('invalidates correctly', async () => {
184
+ it('invalidates correctly with RedisCacheInvalidationStrategy.CURRENT_CACHE_KEY_VERSION', async () => {
159
185
  const mockRedisClient = mock<Redis>();
160
186
  when(mockRedisClient.del()).thenResolve(1);
161
187
 
@@ -166,14 +192,43 @@ describe(GenericRedisCacher, () => {
166
192
  cacheKeyPrefix: 'hello-',
167
193
  ttlSecondsPositive: 1,
168
194
  ttlSecondsNegative: 2,
195
+ invalidationStrategy: RedisCacheInvalidationStrategy.CURRENT_CACHE_KEY_VERSION,
169
196
  },
170
197
  entityConfiguration,
171
198
  );
172
- const cacheKey = genericCacher['makeCacheKey']('id', 'wat');
199
+ const cacheKeys = genericCacher['makeCacheKeysForInvalidation'](
200
+ new SingleFieldHolder('id'),
201
+ new SingleFieldValueHolder('wat'),
202
+ );
203
+ expect(cacheKeys).toHaveLength(1);
204
+
205
+ await genericCacher.invalidateManyAsync(cacheKeys);
206
+ verify(mockRedisClient.del(...cacheKeys)).once();
207
+ });
208
+
209
+ it('invalidates correctly with RedisCacheInvalidationStrategy.SURROUNDING_CACHE_KEY_VERSIONS', async () => {
210
+ const mockRedisClient = mock<Redis>();
211
+ when(mockRedisClient.del()).thenResolve(1);
173
212
 
174
- await genericCacher.invalidateManyAsync([cacheKey]);
213
+ const genericCacher = new GenericRedisCacher(
214
+ {
215
+ redisClient: instance(mockRedisClient),
216
+ makeKeyFn: (...parts) => parts.join(':'),
217
+ cacheKeyPrefix: 'hello-',
218
+ ttlSecondsPositive: 1,
219
+ ttlSecondsNegative: 2,
220
+ invalidationStrategy: RedisCacheInvalidationStrategy.SURROUNDING_CACHE_KEY_VERSIONS,
221
+ },
222
+ entityConfiguration,
223
+ );
224
+ const cacheKeys = genericCacher['makeCacheKeysForInvalidation'](
225
+ new SingleFieldHolder('id'),
226
+ new SingleFieldValueHolder('wat'),
227
+ );
228
+ expect(cacheKeys).toHaveLength(3);
175
229
 
176
- verify(mockRedisClient.del(cacheKey)).once();
230
+ await genericCacher.invalidateManyAsync(cacheKeys);
231
+ verify(mockRedisClient.del(...cacheKeys)).once();
177
232
  });
178
233
 
179
234
  it('returns when passed empty array of fieldValues', async () => {
@@ -184,6 +239,7 @@ describe(GenericRedisCacher, () => {
184
239
  cacheKeyPrefix: 'hello-',
185
240
  ttlSecondsPositive: 1,
186
241
  ttlSecondsNegative: 2,
242
+ invalidationStrategy: RedisCacheInvalidationStrategy.CURRENT_CACHE_KEY_VERSION,
187
243
  },
188
244
  entityConfiguration,
189
245
  );
package/src/index.ts CHANGED
@@ -9,3 +9,4 @@ export * from './GenericRedisCacher';
9
9
  export { default as RedisCacheAdapterProvider } from './RedisCacheAdapterProvider';
10
10
  export * from './RedisCommon';
11
11
  export { default as wrapNativeRedisCallAsync } from './errors/wrapNativeRedisCallAsync';
12
+ export * from './utils/getSurroundingCacheKeyVersionsForInvalidation';
@@ -0,0 +1,9 @@
1
+ import { getSurroundingCacheKeyVersionsForInvalidation } from '../getSurroundingCacheKeyVersionsForInvalidation';
2
+
3
+ describe(getSurroundingCacheKeyVersionsForInvalidation, () => {
4
+ it('returns the correct cache key versions to invalidate', () => {
5
+ expect(getSurroundingCacheKeyVersionsForInvalidation(0)).toEqual([0, 1]);
6
+ expect(getSurroundingCacheKeyVersionsForInvalidation(1)).toEqual([0, 1, 2]);
7
+ expect(getSurroundingCacheKeyVersionsForInvalidation(2)).toEqual([1, 2, 3]);
8
+ });
9
+ });
@@ -0,0 +1,9 @@
1
+ export function getSurroundingCacheKeyVersionsForInvalidation(
2
+ cacheKeyVersion: number,
3
+ ): readonly number[] {
4
+ return [
5
+ ...(cacheKeyVersion === 0 ? [] : [cacheKeyVersion - 1]),
6
+ cacheKeyVersion,
7
+ cacheKeyVersion + 1,
8
+ ];
9
+ }
@@ -1,122 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- const batcher_1 = require("@expo/batcher");
7
- const entity_1 = require("@expo/entity");
8
- const invariant_1 = __importDefault(require("invariant"));
9
- const ioredis_1 = __importDefault(require("ioredis"));
10
- const nullthrows_1 = __importDefault(require("nullthrows"));
11
- const url_1 = require("url");
12
- const uuid_1 = require("uuid");
13
- const GenericRedisCacher_1 = __importDefault(require("../GenericRedisCacher"));
14
- const RedisTestEntity_1 = __importDefault(require("../testfixtures/RedisTestEntity"));
15
- const createRedisIntegrationTestEntityCompanionProvider_1 = require("../testfixtures/createRedisIntegrationTestEntityCompanionProvider");
16
- class BatchedRedis {
17
- redis;
18
- mgetBatcher = new batcher_1.Batcher(this.batchMgetAsync.bind(this), {
19
- maxBatchInterval: 0,
20
- });
21
- constructor(redis) {
22
- this.redis = redis;
23
- }
24
- async batchMgetAsync(keySets) {
25
- // ordered distinct keys to fetch
26
- const allKeysToFetch = [...new Set(keySets.flat())];
27
- // fetch the distinct keys
28
- const allResults = await this.redis.mget(...allKeysToFetch);
29
- // put them into a map for fast lookup
30
- const keysToResults = (0, entity_1.zipToMap)(allKeysToFetch, allResults);
31
- // re-associate them with original key sets
32
- return keySets.map((keySet) => keySet.map((key) => {
33
- const result = keysToResults.get(key);
34
- (0, invariant_1.default)(result !== undefined, 'result should not be undefined');
35
- return result;
36
- }));
37
- }
38
- // eslint-disable-next-line @typescript-eslint/naming-convention
39
- async mget(...args) {
40
- return await this.mgetBatcher.batchAsync(args);
41
- }
42
- multi() {
43
- return this.redis.multi();
44
- }
45
- // eslint-disable-next-line @typescript-eslint/naming-convention
46
- async del(...args) {
47
- await this.redis.del(...args);
48
- }
49
- }
50
- describe(GenericRedisCacher_1.default, () => {
51
- const redis = new ioredis_1.default(new url_1.URL(process.env['REDIS_URL']).toString());
52
- const redisClient = new BatchedRedis(redis);
53
- let genericRedisCacheContext;
54
- beforeAll(() => {
55
- genericRedisCacheContext = {
56
- redisClient,
57
- makeKeyFn(...parts) {
58
- const delimiter = ':';
59
- const escapedParts = parts.map((part) => part.replace('\\', '\\\\').replace(delimiter, `\\${delimiter}`));
60
- return escapedParts.join(delimiter);
61
- },
62
- cacheKeyPrefix: 'test-',
63
- ttlSecondsPositive: 86400, // 1 day
64
- ttlSecondsNegative: 600, // 10 minutes
65
- };
66
- });
67
- beforeEach(async () => {
68
- await redis.flushdb();
69
- });
70
- afterAll(async () => {
71
- redis.disconnect();
72
- });
73
- it('has correct caching behavior', async () => {
74
- // simulate two requests
75
- const viewerContext = new entity_1.ViewerContext((0, createRedisIntegrationTestEntityCompanionProvider_1.createRedisIntegrationTestEntityCompanionProvider)(genericRedisCacheContext));
76
- const mgetSpy = jest.spyOn(redis, 'mget');
77
- const genericCacher = viewerContext.entityCompanionProvider.getCompanionForEntity(RedisTestEntity_1.default)['tableDataCoordinator']['cacheAdapter']['genericCacher'];
78
- const cacheKeyMaker = genericCacher['makeCacheKey'].bind(genericCacher);
79
- const entity1Created = await RedisTestEntity_1.default.creator(viewerContext)
80
- .setField('name', 'blah')
81
- .createAsync();
82
- // loading an entity should put it in cache. load by multiple requests and multiple fields in same tick to ensure batch works
83
- mgetSpy.mockClear();
84
- const viewerContext1 = new entity_1.ViewerContext((0, createRedisIntegrationTestEntityCompanionProvider_1.createRedisIntegrationTestEntityCompanionProvider)(genericRedisCacheContext));
85
- const viewerContext2 = new entity_1.ViewerContext((0, createRedisIntegrationTestEntityCompanionProvider_1.createRedisIntegrationTestEntityCompanionProvider)(genericRedisCacheContext));
86
- const viewerContext3 = new entity_1.ViewerContext((0, createRedisIntegrationTestEntityCompanionProvider_1.createRedisIntegrationTestEntityCompanionProvider)(genericRedisCacheContext));
87
- const [entity1, entity2, entity3] = await Promise.all([
88
- RedisTestEntity_1.default.loader(viewerContext1).loadByIDAsync(entity1Created.getID()),
89
- RedisTestEntity_1.default.loader(viewerContext2).loadByIDAsync(entity1Created.getID()),
90
- RedisTestEntity_1.default.loader(viewerContext3).loadByFieldEqualingAsync('name', entity1Created.getField('name')),
91
- ]);
92
- expect(mgetSpy).toHaveBeenCalledTimes(1);
93
- expect(mgetSpy.mock.calls[0]).toHaveLength(2); // should dedupe the first two loads
94
- expect(entity1.getID()).toEqual(entity2.getID());
95
- expect(entity2.getID()).toEqual((0, nullthrows_1.default)(entity3).getID());
96
- const cacheKeyEntity1 = cacheKeyMaker('id', entity1Created.getID());
97
- const cachedJSON = await redis.get(cacheKeyEntity1);
98
- const cachedValue = JSON.parse(cachedJSON);
99
- expect(cachedValue).toMatchObject({
100
- id: entity1.getID(),
101
- name: 'blah',
102
- });
103
- const cacheKeyEntity1NameField = cacheKeyMaker('name', entity1Created.getField('name'));
104
- await RedisTestEntity_1.default.loader(viewerContext).loadByFieldEqualingAsync('name', entity1Created.getField('name'));
105
- await expect(redis.get(cacheKeyEntity1NameField)).resolves.toEqual(cachedJSON);
106
- // simulate non existent db fetch, should write negative result ('') to cache
107
- const nonExistentId = (0, uuid_1.v4)();
108
- const entityNonExistentResult = await RedisTestEntity_1.default.loaderWithAuthorizationResults(viewerContext).loadByIDAsync(nonExistentId);
109
- expect(entityNonExistentResult.ok).toBe(false);
110
- const cacheKeyNonExistent = cacheKeyMaker('id', nonExistentId);
111
- const nonExistentCachedValue = await redis.get(cacheKeyNonExistent);
112
- expect(nonExistentCachedValue).toEqual('');
113
- // load again through entities framework to ensure it reads negative result
114
- const entityNonExistentResult2 = await RedisTestEntity_1.default.loaderWithAuthorizationResults(viewerContext).loadByIDAsync(nonExistentId);
115
- expect(entityNonExistentResult2.ok).toBe(false);
116
- // invalidate from cache to ensure it invalidates correctly in both caches
117
- await RedisTestEntity_1.default.loaderUtils(viewerContext).invalidateFieldsAsync(entity1.getAllFields());
118
- await expect(redis.get(cacheKeyEntity1)).resolves.toBeNull();
119
- await expect(redis.get(cacheKeyEntity1NameField)).resolves.toBeNull();
120
- });
121
- });
122
- //# sourceMappingURL=BatchedRedisCacheAdapter-integration-test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"BatchedRedisCacheAdapter-integration-test.js","sourceRoot":"","sources":["../../src/__integration-tests__/BatchedRedisCacheAdapter-integration-test.ts"],"names":[],"mappings":"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"}
@@ -1,93 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- const entity_1 = require("@expo/entity");
7
- const results_1 = require("@expo/results");
8
- const ioredis_1 = __importDefault(require("ioredis"));
9
- const url_1 = require("url");
10
- const uuid_1 = require("uuid");
11
- const GenericRedisCacher_1 = __importDefault(require("../GenericRedisCacher"));
12
- const RedisTestEntity_1 = __importDefault(require("../testfixtures/RedisTestEntity"));
13
- const createRedisIntegrationTestEntityCompanionProvider_1 = require("../testfixtures/createRedisIntegrationTestEntityCompanionProvider");
14
- class TestViewerContext extends entity_1.ViewerContext {
15
- }
16
- describe(GenericRedisCacher_1.default, () => {
17
- let genericRedisCacheContext;
18
- beforeAll(() => {
19
- genericRedisCacheContext = {
20
- redisClient: new ioredis_1.default(new url_1.URL(process.env['REDIS_URL']).toString()),
21
- makeKeyFn(...parts) {
22
- const delimiter = ':';
23
- const escapedParts = parts.map((part) => part.replace('\\', '\\\\').replace(delimiter, `\\${delimiter}`));
24
- return escapedParts.join(delimiter);
25
- },
26
- cacheKeyPrefix: 'test-',
27
- ttlSecondsPositive: 86400, // 1 day
28
- ttlSecondsNegative: 600, // 10 minutes
29
- };
30
- });
31
- beforeEach(async () => {
32
- await genericRedisCacheContext.redisClient.flushdb();
33
- });
34
- afterAll(async () => {
35
- genericRedisCacheContext.redisClient.disconnect();
36
- });
37
- it('has correct caching behavior', async () => {
38
- const viewerContext = new TestViewerContext((0, createRedisIntegrationTestEntityCompanionProvider_1.createRedisIntegrationTestEntityCompanionProvider)(genericRedisCacheContext));
39
- const genericCacher = viewerContext.entityCompanionProvider.getCompanionForEntity(RedisTestEntity_1.default)['tableDataCoordinator']['cacheAdapter']['genericCacher'];
40
- const cacheKeyMaker = genericCacher['makeCacheKey'].bind(genericCacher);
41
- const entity1Created = await RedisTestEntity_1.default.creator(viewerContext)
42
- .setField('name', 'blah')
43
- .createAsync();
44
- // loading an entity should put it in cache
45
- const entity1 = await RedisTestEntity_1.default.loader(viewerContext).loadByIDAsync(entity1Created.getID());
46
- const cachedJSON = await genericRedisCacheContext.redisClient.get(cacheKeyMaker('id', entity1.getID()));
47
- const cachedValue = JSON.parse(cachedJSON);
48
- expect(cachedValue).toMatchObject({
49
- id: entity1.getID(),
50
- name: 'blah',
51
- });
52
- // simulate non existent db fetch, should write negative result ('') to cache
53
- const nonExistentId = (0, uuid_1.v4)();
54
- const entityNonExistentResult = await RedisTestEntity_1.default.loaderWithAuthorizationResults(viewerContext).loadByIDAsync(nonExistentId);
55
- expect(entityNonExistentResult.ok).toBe(false);
56
- const nonExistentCachedValue = await genericRedisCacheContext.redisClient.get(cacheKeyMaker('id', nonExistentId));
57
- expect(nonExistentCachedValue).toEqual('');
58
- // load again through entities framework to ensure it reads negative result
59
- const entityNonExistentResult2 = await RedisTestEntity_1.default.loaderWithAuthorizationResults(viewerContext).loadByIDAsync(nonExistentId);
60
- expect(entityNonExistentResult2.ok).toBe(false);
61
- // invalidate from cache to ensure it invalidates correctly
62
- await RedisTestEntity_1.default.loaderUtils(viewerContext).invalidateFieldsAsync(entity1.getAllFields());
63
- const cachedValueNull = await genericRedisCacheContext.redisClient.get(cacheKeyMaker('id', entity1.getID()));
64
- expect(cachedValueNull).toBe(null);
65
- });
66
- it('caches and restores date fields', async () => {
67
- const viewerContext = new TestViewerContext((0, createRedisIntegrationTestEntityCompanionProvider_1.createRedisIntegrationTestEntityCompanionProvider)(genericRedisCacheContext));
68
- const date = new Date();
69
- const entity1 = await (0, results_1.enforceAsyncResult)(RedisTestEntity_1.default.creatorWithAuthorizationResults(viewerContext)
70
- .setField('dateField', date)
71
- .createAsync());
72
- expect(entity1.getField('dateField')).toEqual(date);
73
- const entity2 = await RedisTestEntity_1.default.loader(viewerContext).loadByIDAsync(entity1.getID());
74
- expect(entity2.getField('dateField')).toEqual(date);
75
- // simulate new request
76
- const vc2 = new TestViewerContext((0, createRedisIntegrationTestEntityCompanionProvider_1.createRedisIntegrationTestEntityCompanionProvider)(genericRedisCacheContext));
77
- const entity3 = await RedisTestEntity_1.default.loader(vc2).loadByIDAsync(entity1.getID());
78
- expect(entity3.getField('dateField')).toEqual(date);
79
- });
80
- it('caches and restores empty string field keys', async () => {
81
- const viewerContext = new TestViewerContext((0, createRedisIntegrationTestEntityCompanionProvider_1.createRedisIntegrationTestEntityCompanionProvider)(genericRedisCacheContext));
82
- const entity1 = await (0, results_1.enforceAsyncResult)(RedisTestEntity_1.default.creatorWithAuthorizationResults(viewerContext)
83
- .setField('name', '')
84
- .createAsync());
85
- const entity2 = await RedisTestEntity_1.default.loader(viewerContext).loadByFieldEqualingAsync('name', '');
86
- expect(entity2?.getID()).toEqual(entity1.getID());
87
- // simulate new request
88
- const vc2 = new TestViewerContext((0, createRedisIntegrationTestEntityCompanionProvider_1.createRedisIntegrationTestEntityCompanionProvider)(genericRedisCacheContext));
89
- const entity3 = await RedisTestEntity_1.default.loader(vc2).loadByFieldEqualingAsync('name', '');
90
- expect(entity3?.getID()).toEqual(entity1.getID());
91
- });
92
- });
93
- //# sourceMappingURL=GenericRedisCacher-full-integration-test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"GenericRedisCacher-full-integration-test.js","sourceRoot":"","sources":["../../src/__integration-tests__/GenericRedisCacher-full-integration-test.ts"],"names":[],"mappings":"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"}