@travetto/cache 4.0.7 → 4.1.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/cache",
3
- "version": "4.0.7",
3
+ "version": "4.1.0",
4
4
  "description": "Caching functionality with decorators for declarative use.",
5
5
  "keywords": [
6
6
  "typescript",
@@ -25,12 +25,12 @@
25
25
  "directory": "module/cache"
26
26
  },
27
27
  "dependencies": {
28
- "@travetto/di": "^4.0.7",
29
- "@travetto/model": "^4.0.7"
28
+ "@travetto/di": "^4.1.0",
29
+ "@travetto/model": "^4.1.0"
30
30
  },
31
31
  "peerDependencies": {
32
- "@travetto/test": "^4.0.7",
33
- "@travetto/transformer": "^4.0.4"
32
+ "@travetto/test": "^4.1.0",
33
+ "@travetto/transformer": "^4.1.0"
34
34
  },
35
35
  "peerDependenciesMeta": {
36
36
  "@travetto/transformer": {
package/src/service.ts CHANGED
@@ -1,8 +1,8 @@
1
- import { ExpiresAt, Model, ModelExpirySupport, NotFoundError } from '@travetto/model';
1
+ import { ExpiresAt, Index, Model, ModelExpirySupport, NotFoundError } from '@travetto/model';
2
2
  import { Text } from '@travetto/schema';
3
3
  import { Inject, Injectable } from '@travetto/di';
4
- import { Env } from '@travetto/base';
5
- import { isStorageSupported } from '@travetto/model/src/internal/service/common';
4
+ import { AppError, Env } from '@travetto/base';
5
+ import { isIndexedSupported, isStorageSupported } from '@travetto/model/src/internal/service/common';
6
6
 
7
7
  import { CacheError } from './error';
8
8
  import { CacheUtil } from './util';
@@ -12,11 +12,17 @@ export const CacheModelⲐ = Symbol.for('@travetto/cache:model');
12
12
 
13
13
  const INFINITE_MAX_AGE = '5000-01-01';
14
14
 
15
+ @Index({
16
+ name: 'keySpace',
17
+ type: 'unsorted',
18
+ fields: [{ keySpace: 1 }]
19
+ })
15
20
  @Model({ autoCreate: false })
16
21
  export class CacheRecord {
17
22
  id: string;
18
23
  @Text()
19
24
  entry: string;
25
+ keySpace: string;
20
26
  @ExpiresAt()
21
27
  expiresAt: Date;
22
28
  issuedAt: Date;
@@ -77,13 +83,14 @@ export class CacheService {
77
83
  * @param maxAge Max age in ms
78
84
  * @returns
79
85
  */
80
- async set(id: string, entry: unknown, maxAge?: number): Promise<unknown> {
86
+ async set(id: string, keySpace: string, entry: unknown, maxAge?: number): Promise<unknown> {
81
87
  const entryText = CacheUtil.toSafeJSON(entry);
82
88
 
83
89
  const store = await this.#modelService.upsert(CacheRecord,
84
90
  CacheRecord.from({
85
91
  id,
86
92
  entry: entryText!,
93
+ keySpace,
87
94
  expiresAt: new Date(maxAge ? maxAge + Date.now() : INFINITE_MAX_AGE),
88
95
  issuedAt: new Date()
89
96
  }),
@@ -100,6 +107,22 @@ export class CacheService {
100
107
  await this.#modelService.delete(CacheRecord, id);
101
108
  }
102
109
 
110
+ /**
111
+ * Remove all entries by key space
112
+ * @param id
113
+ */
114
+ async deleteAll(keySpace: string): Promise<void> {
115
+ if (isIndexedSupported(this.#modelService)) {
116
+ const removes: Promise<void>[] = [];
117
+ for await (const item of this.#modelService.listByIndex(CacheRecord, 'keySpace', { keySpace })) {
118
+ removes.push(this.#modelService.delete(CacheRecord, item.id));
119
+ }
120
+ await Promise.all(removes);
121
+ } else {
122
+ throw new AppError('Unable to delete all on an un-indexed database', 'general');
123
+ }
124
+ }
125
+
103
126
  /**
104
127
  * Purge the cache store of all data, if supported
105
128
  */
@@ -146,7 +169,7 @@ export class CacheService {
146
169
 
147
170
  if (res === undefined) {
148
171
  const data = await fn.apply(target, params);
149
- res = await this.set(id, data, config.maxAge);
172
+ res = await this.set(id, config.keySpace!, data, config.maxAge);
150
173
  }
151
174
 
152
175
  if (config.reinstate) { // Reinstate result value if needed
@@ -6,6 +6,7 @@ import { ModelExpirySupport } from '@travetto/model';
6
6
  import { Inject, Injectable } from '@travetto/di';
7
7
  import { InjectableSuite } from '@travetto/di/support/test/suite';
8
8
  import { ModelSuite } from '@travetto/model/support/test/suite';
9
+ import { isIndexedSupported } from '@travetto/model/src/internal/service/common';
9
10
  import { Class } from '@travetto/base';
10
11
  import { Schema } from '@travetto/schema';
11
12
 
@@ -72,6 +73,12 @@ class SampleService {
72
73
  await timers.setTimeout(100);
73
74
  return true;
74
75
  }
76
+
77
+ async deleteAllUsers() {
78
+ this.source.deleteAll('user.id');
79
+ await timers.setTimeout(100);
80
+ return true;
81
+ }
75
82
  }
76
83
 
77
84
  @Suite()
@@ -209,4 +216,43 @@ export abstract class CacheServiceSuite {
209
216
 
210
217
  await assert.doesNotReject(() => service.deleteUser('200'));
211
218
  }
219
+
220
+ @Test()
221
+ async allEviction() {
222
+ if (!isIndexedSupported(this.serviceClass.prototype)) {
223
+ return;
224
+ }
225
+
226
+ const service = await this.testService;
227
+
228
+ // Prime cache
229
+ for (let i = 0; i < 10; i++) {
230
+ const start = Date.now();
231
+ await service.getUser(`${i}`);
232
+ assert((Date.now() - start) >= 100);
233
+ }
234
+
235
+ // Read cache
236
+ for (let i = 0; i < 10; i++) {
237
+ const start = Date.now();
238
+ await service.getUser(`${i}`);
239
+ assert((Date.now() - start) <= (this.baseLatency + 100));
240
+ }
241
+
242
+ await service.deleteAllUsers();
243
+
244
+ // Prime cache
245
+ for (let i = 0; i < 10; i++) {
246
+ const start = Date.now();
247
+ await service.getUser(`${i}`);
248
+ assert((Date.now() - start) >= 100);
249
+ }
250
+
251
+ // Read cache
252
+ for (let i = 0; i < 10; i++) {
253
+ const start = Date.now();
254
+ await service.getUser(`${i}`);
255
+ assert((Date.now() - start) <= (this.baseLatency + 100));
256
+ }
257
+ }
212
258
  }