@travetto/cache 8.0.0-alpha.9 → 8.0.1
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/README.md +18 -22
- package/__index__.ts +2 -2
- package/package.json +17 -17
- package/src/decorator.ts +17 -5
- package/src/error.ts +1 -1
- package/src/service.ts +24 -17
- package/src/types.ts +0 -1
- package/src/util.ts +1 -2
- package/support/test/service.ts +18 -19
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ npm install @travetto/cache
|
|
|
13
13
|
yarn add @travetto/cache
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
-
Provides a foundational structure for integrating caching at the method level.
|
|
16
|
+
Provides a foundational structure for integrating caching at the method level. This allows for easy extension with a variety of providers, and is usable with or without [Dependency Injection](https://github.com/travetto/travetto/tree/main/module/di#readme "Dependency registration/management and injection support."). The code aims to handle use cases surrounding common/basic usage.
|
|
17
17
|
|
|
18
18
|
The cache module requires an [Expiry](https://github.com/travetto/travetto/tree/main/module/model/src/types/expiry.ts#L10) to provide functionality for reading and writing streams. You can use any existing providers to serve as your [Expiry](https://github.com/travetto/travetto/tree/main/module/model/src/types/expiry.ts#L10), or you can roll your own.
|
|
19
19
|
|
|
@@ -38,16 +38,16 @@ Currently, the following are packages that provide [Expiry](https://github.com/t
|
|
|
38
38
|
* [File Model Support](https://github.com/travetto/travetto/tree/main/module/model-file#readme "File system backing for the travetto model module.") - @travetto/model-file
|
|
39
39
|
|
|
40
40
|
## Decorators
|
|
41
|
-
The caching framework provides method decorators that enables simple use cases.
|
|
41
|
+
The caching framework provides method decorators that enables simple use cases. One of the requirements to use the caching decorators is that the method arguments, and return values need to be serializable into [JSON](https://www.json.org). Any other data types are not currently supported and would require either manual usage of the caching services directly, or specification of serialization/deserialization routines in the cache config.
|
|
42
42
|
|
|
43
|
-
Additionally, to use the decorators you will need to have a [CacheService](https://github.com/travetto/travetto/tree/main/module/cache/src/service.ts#
|
|
43
|
+
Additionally, to use the decorators you will need to have a [CacheService](https://github.com/travetto/travetto/tree/main/module/cache/src/service.ts#L35) object accessible on the class instance. This can be dependency injected, or manually constructed. The decorators will detect the field at time of method execution, which decouples construction of your class from the cache construction.
|
|
44
44
|
|
|
45
|
-
[@Cache](https://github.com/travetto/travetto/tree/main/module/cache/src/decorator.ts#L12) is a decorator that will cache all successful results, keyed by a computation based on the method arguments.
|
|
45
|
+
[@Cache](https://github.com/travetto/travetto/tree/main/module/cache/src/decorator.ts#L12) is a decorator that will cache all successful results, keyed by a computation based on the method arguments. Given the desire for supporting remote caches (e.g. [redis](https://redis.io), [memcached](https://memcached.org)), only asynchronous methods are supported.
|
|
46
46
|
|
|
47
47
|
**Code: Using decorators to cache expensive async call**
|
|
48
48
|
```typescript
|
|
49
|
-
import { MemoryModelService } from '@travetto/model-memory';
|
|
50
49
|
import { Cache, CacheService } from '@travetto/cache';
|
|
50
|
+
import { MemoryModelService } from '@travetto/model-memory';
|
|
51
51
|
|
|
52
52
|
async function request(url: string): Promise<string> {
|
|
53
53
|
let value: string;
|
|
@@ -56,10 +56,7 @@ async function request(url: string): Promise<string> {
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
export class Worker {
|
|
59
|
-
|
|
60
|
-
myCache = new CacheService(
|
|
61
|
-
new MemoryModelService({ namespace: '' })
|
|
62
|
-
);
|
|
59
|
+
myCache = new CacheService(new MemoryModelService({ namespace: '' }));
|
|
63
60
|
|
|
64
61
|
@Cache('myCache', '1s')
|
|
65
62
|
async calculateExpensiveResult(expression: string): Promise<string> {
|
|
@@ -74,26 +71,25 @@ The [@Cache](https://github.com/travetto/travetto/tree/main/module/cache/src/dec
|
|
|
74
71
|
* `name` the field name of the current class which points to the desired cache source.
|
|
75
72
|
* `config` the additional/optional config options, on a per invocation basis
|
|
76
73
|
|
|
77
|
-
* `keySpace` the key space within the cache.
|
|
78
|
-
* `key` the function
|
|
79
|
-
* `params` the function used to determine the inputs for computing the cache key.
|
|
80
|
-
* `maxAge` the number of milliseconds will hold the value before considering the cache entry to be invalid.
|
|
81
|
-
* `extendOnAccess` determines if the cache timeout should be extended on access.
|
|
82
|
-
* `serialize` the function to execute before storing a cacheable value.
|
|
83
|
-
* `reinstate` the function to execute on return of a cached value.
|
|
74
|
+
* `keySpace` the key space within the cache. Defaults to class name plus method name.
|
|
75
|
+
* `key` the function will use the inputs to determine the cache key, defaults to all params `JSON.stringify`ied
|
|
76
|
+
* `params` the function used to determine the inputs for computing the cache key. This is an easier place to start to define what parameters are important in ,caching. This defaults to all inputs.
|
|
77
|
+
* `maxAge` the number of milliseconds will hold the value before considering the cache entry to be invalid. By default values will live infinitely.
|
|
78
|
+
* `extendOnAccess` determines if the cache timeout should be extended on access. This only applies to cache values that have specified a `maxAge`.
|
|
79
|
+
* `serialize` the function to execute before storing a cacheable value. This allows for any custom data modification needed to persist as a string properly.
|
|
80
|
+
* `reinstate` the function to execute on return of a cached value. This allows for any necessary operations to conform to expected output (e.g. re-establishing class instances, etc.). This method should not be used often, as the return values of the methods should naturally serialize to/from `JSON` and the values should be usable either way.
|
|
84
81
|
|
|
85
82
|
### EvictCache
|
|
86
|
-
Additionally, there is support for planned eviction via the [@EvictCache](https://github.com/travetto/travetto/tree/main/module/cache/src/decorator.ts#
|
|
83
|
+
Additionally, there is support for planned eviction via the [@EvictCache](https://github.com/travetto/travetto/tree/main/module/cache/src/decorator.ts#L55) decorator. On successful execution of a method with this decorator, the matching keySpace/key value will be evicted from the cache. This requires coordination between multiple methods, to use the same `keySpace` and `key` to compute the expected key.
|
|
87
84
|
|
|
88
85
|
**Code: Using decorators to cache/evict user access**
|
|
89
86
|
```typescript
|
|
87
|
+
import { Cache, CacheService, EvictCache } from '@travetto/cache';
|
|
90
88
|
import { MemoryModelService } from '@travetto/model-memory';
|
|
91
|
-
import { Cache, EvictCache, CacheService } from '@travetto/cache';
|
|
92
89
|
|
|
93
|
-
class User {
|
|
90
|
+
class User {}
|
|
94
91
|
|
|
95
92
|
export class UserService {
|
|
96
|
-
|
|
97
93
|
myCache = new CacheService(new MemoryModelService({ namespace: '' }));
|
|
98
94
|
database: {
|
|
99
95
|
lookupUser(id: string): Promise<User>;
|
|
@@ -119,14 +115,14 @@ export class UserService {
|
|
|
119
115
|
```
|
|
120
116
|
|
|
121
117
|
## Extending the Cache Service
|
|
122
|
-
By design, the [CacheService](https://github.com/travetto/travetto/tree/main/module/cache/src/service.ts#
|
|
118
|
+
By design, the [CacheService](https://github.com/travetto/travetto/tree/main/module/cache/src/service.ts#L35) relies solely on the [Data Modeling Support](https://github.com/travetto/travetto/tree/main/module/model#readme "Datastore abstraction for core operations.") module. Specifically on the [Expiry](https://github.com/travetto/travetto/tree/main/module/model/src/types/expiry.ts#L10). This combines basic support for CRUD as well as knowledge of how to manage content that expires. Any model service that honors these contracts is a valid candidate to power the [CacheService](https://github.com/travetto/travetto/tree/main/module/cache/src/service.ts#L35). The [CacheService](https://github.com/travetto/travetto/tree/main/module/cache/src/service.ts#L35) is expecting the model service to be registered using the @travetto/cache:model:
|
|
123
119
|
|
|
124
120
|
**Code: Registering a Custom Model Source**
|
|
125
121
|
```typescript
|
|
122
|
+
import { CacheModelSymbol } from '@travetto/cache';
|
|
126
123
|
import { InjectableFactory } from '@travetto/di';
|
|
127
124
|
import type { ModelExpirySupport } from '@travetto/model';
|
|
128
125
|
import { MemoryModelService } from '@travetto/model-memory';
|
|
129
|
-
import { CacheModelSymbol } from '@travetto/cache';
|
|
130
126
|
|
|
131
127
|
class Config {
|
|
132
128
|
@InjectableFactory(CacheModelSymbol)
|
package/__index__.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,37 +1,40 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/cache",
|
|
3
|
-
"version": "8.0.
|
|
4
|
-
"type": "module",
|
|
3
|
+
"version": "8.0.1",
|
|
5
4
|
"description": "Caching functionality with decorators for declarative use.",
|
|
6
5
|
"keywords": [
|
|
7
|
-
"
|
|
8
|
-
"travetto",
|
|
6
|
+
"cache",
|
|
9
7
|
"decorator",
|
|
10
|
-
"
|
|
8
|
+
"travetto",
|
|
9
|
+
"typescript"
|
|
11
10
|
],
|
|
12
11
|
"homepage": "https://travetto.io",
|
|
13
12
|
"license": "MIT",
|
|
14
13
|
"author": {
|
|
15
|
-
"
|
|
16
|
-
"
|
|
14
|
+
"name": "Travetto Framework",
|
|
15
|
+
"email": "travetto.framework@gmail.com"
|
|
16
|
+
},
|
|
17
|
+
"repository": {
|
|
18
|
+
"url": "git+https://github.com/travetto/travetto.git",
|
|
19
|
+
"directory": "module/cache"
|
|
17
20
|
},
|
|
18
21
|
"files": [
|
|
19
22
|
"__index__.ts",
|
|
20
23
|
"src",
|
|
21
24
|
"support"
|
|
22
25
|
],
|
|
26
|
+
"type": "module",
|
|
23
27
|
"main": "__index__.ts",
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"directory": "module/cache"
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"access": "public"
|
|
27
30
|
},
|
|
28
31
|
"dependencies": {
|
|
29
|
-
"@travetto/di": "^8.0.
|
|
30
|
-
"@travetto/model": "^8.0.
|
|
32
|
+
"@travetto/di": "^8.0.1",
|
|
33
|
+
"@travetto/model": "^8.0.1"
|
|
31
34
|
},
|
|
32
35
|
"peerDependencies": {
|
|
33
|
-
"@travetto/test": "^8.0.
|
|
34
|
-
"@travetto/transformer": "^8.0.
|
|
36
|
+
"@travetto/test": "^8.0.1",
|
|
37
|
+
"@travetto/transformer": "^8.0.1"
|
|
35
38
|
},
|
|
36
39
|
"peerDependenciesMeta": {
|
|
37
40
|
"@travetto/transformer": {
|
|
@@ -43,8 +46,5 @@
|
|
|
43
46
|
},
|
|
44
47
|
"travetto": {
|
|
45
48
|
"displayName": "Caching"
|
|
46
|
-
},
|
|
47
|
-
"publishConfig": {
|
|
48
|
-
"access": "public"
|
|
49
49
|
}
|
|
50
50
|
}
|
package/src/decorator.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { castTo, type MethodDescriptor, type TimeSpan, TimeUtil } from '@travetto/runtime';
|
|
2
2
|
|
|
3
3
|
import type { CacheService } from './service.ts';
|
|
4
|
-
import { type
|
|
4
|
+
import { type CacheAware, type CacheConfig, CacheConfigSymbol, type CoreCacheConfig, EvictConfigSymbol } from './types.ts';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Indicates a method is intended to cache. The return type must be properly serializable
|
|
@@ -9,10 +9,16 @@ import { type CoreCacheConfig, type CacheConfig, type CacheAware, CacheConfigSym
|
|
|
9
9
|
* @param config The additional cache configuration
|
|
10
10
|
* @kind decorator
|
|
11
11
|
*/
|
|
12
|
-
export function Cache<F extends string, U extends Record<F, CacheService>>(
|
|
12
|
+
export function Cache<F extends string, U extends Record<F, CacheService>>(
|
|
13
|
+
field: F,
|
|
14
|
+
maxAge: number | TimeSpan,
|
|
15
|
+
config?: Omit<CacheConfig, 'maxAge'>
|
|
16
|
+
): MethodDecorator;
|
|
13
17
|
export function Cache<F extends string, U extends Record<F, CacheService>>(field: F, input?: CacheConfig): MethodDecorator;
|
|
14
18
|
export function Cache<F extends string, U extends Record<F, CacheService>>(
|
|
15
|
-
field: F,
|
|
19
|
+
field: F,
|
|
20
|
+
input?: number | TimeSpan | CacheConfig,
|
|
21
|
+
config: Exclude<CacheConfig, 'maxAge'> = {}
|
|
16
22
|
): MethodDecorator {
|
|
17
23
|
if (input !== undefined) {
|
|
18
24
|
if (typeof input === 'string' || typeof input === 'number') {
|
|
@@ -21,12 +27,17 @@ export function Cache<F extends string, U extends Record<F, CacheService>>(
|
|
|
21
27
|
config = input;
|
|
22
28
|
}
|
|
23
29
|
}
|
|
24
|
-
const decorator = function <R extends Promise<unknown>>(
|
|
30
|
+
const decorator = function <R extends Promise<unknown>>(
|
|
31
|
+
target: U & CacheAware,
|
|
32
|
+
propertyKey: string,
|
|
33
|
+
descriptor: MethodDescriptor<R>
|
|
34
|
+
): void {
|
|
25
35
|
config.keySpace ??= `${target.constructor.name}.${propertyKey}`;
|
|
26
36
|
(target[CacheConfigSymbol] ??= {})[propertyKey] = config;
|
|
27
37
|
const handler = descriptor.value!;
|
|
28
38
|
// Allows for DI to run, as the service will not be bound until after the decorator is run
|
|
29
39
|
descriptor.value = castTo(function (this: typeof target) {
|
|
40
|
+
// biome-ignore lint/complexity/noArguments: We want to use arguments here
|
|
30
41
|
return this[field].cache(this, propertyKey, handler, [...arguments]);
|
|
31
42
|
});
|
|
32
43
|
Object.defineProperty(descriptor.value, 'name', { value: propertyKey, writable: false });
|
|
@@ -48,8 +59,9 @@ export function EvictCache<F extends string, U extends Record<F, CacheService>>(
|
|
|
48
59
|
const handler = descriptor.value!;
|
|
49
60
|
// Allows for DI to run, as the service will not be bound until after the decorator is run
|
|
50
61
|
descriptor.value = castTo(function (this: typeof target) {
|
|
62
|
+
// biome-ignore lint/complexity/noArguments: We want to use arguments here
|
|
51
63
|
return this[field].evict(this, propertyKey, handler, [...arguments]);
|
|
52
64
|
});
|
|
53
65
|
Object.defineProperty(descriptor.value, 'name', { value: propertyKey, writable: false });
|
|
54
66
|
};
|
|
55
|
-
}
|
|
67
|
+
}
|
package/src/error.ts
CHANGED
package/src/service.ts
CHANGED
|
@@ -1,36 +1,38 @@
|
|
|
1
|
-
import { ExpiresAt, Index, Model, type ModelExpirySupport, NotFoundError, ModelStorageUtil, ModelIndexedUtil } from '@travetto/model';
|
|
2
|
-
import { Text } from '@travetto/schema';
|
|
3
1
|
import { Inject, Injectable } from '@travetto/di';
|
|
4
|
-
import {
|
|
2
|
+
import { ExpiresAt, Model, type ModelExpirySupport, ModelStorageUtil, NotFoundError } from '@travetto/model';
|
|
3
|
+
import { ModelIndexedUtil, sortedIndex } from '@travetto/model-indexed';
|
|
4
|
+
import { JSONUtil, RuntimeError, TimeUtil } from '@travetto/runtime';
|
|
5
|
+
import { MaxLength, Text } from '@travetto/schema';
|
|
5
6
|
|
|
6
7
|
import { CacheError } from './error.ts';
|
|
7
|
-
import { CacheUtil } from './util.ts';
|
|
8
8
|
import { type CacheAware, CacheConfigSymbol, CacheModelSymbol, EvictConfigSymbol } from './types.ts';
|
|
9
|
+
import { CacheUtil } from './util.ts';
|
|
9
10
|
|
|
10
11
|
const INFINITE_MAX_AGE = TimeUtil.duration('10y', 'ms');
|
|
11
12
|
|
|
12
|
-
@Index({
|
|
13
|
-
name: 'keySpace',
|
|
14
|
-
type: 'unsorted',
|
|
15
|
-
fields: [{ keySpace: 1 }]
|
|
16
|
-
})
|
|
17
13
|
@Model({ autoCreate: 'production' })
|
|
18
14
|
export class CacheRecord {
|
|
19
15
|
id: string;
|
|
20
16
|
@Text()
|
|
21
17
|
entry: string;
|
|
18
|
+
@MaxLength(500)
|
|
22
19
|
keySpace: string;
|
|
23
20
|
@ExpiresAt()
|
|
24
21
|
expiresAt: Date;
|
|
25
22
|
issuedAt: Date;
|
|
26
23
|
}
|
|
27
24
|
|
|
25
|
+
const keySpaceIndex = sortedIndex(CacheRecord, {
|
|
26
|
+
name: 'keySpace',
|
|
27
|
+
key: { keySpace: true },
|
|
28
|
+
sort: { expiresAt: 1 }
|
|
29
|
+
});
|
|
30
|
+
|
|
28
31
|
/**
|
|
29
32
|
* Cache source
|
|
30
33
|
*/
|
|
31
34
|
@Injectable()
|
|
32
35
|
export class CacheService {
|
|
33
|
-
|
|
34
36
|
#modelService: ModelExpirySupport;
|
|
35
37
|
|
|
36
38
|
constructor(@Inject({ qualifier: CacheModelSymbol, resolution: 'loose' }) modelService: ModelExpirySupport) {
|
|
@@ -48,7 +50,8 @@ export class CacheService {
|
|
|
48
50
|
const delta = expiresAt.getTime() - Date.now();
|
|
49
51
|
const maxAge = expiresAt.getTime() - issuedAt.getTime();
|
|
50
52
|
|
|
51
|
-
if (delta < 0) {
|
|
53
|
+
if (delta < 0) {
|
|
54
|
+
// Expired
|
|
52
55
|
await this.#modelService.delete(CacheRecord, id);
|
|
53
56
|
throw new CacheError('Key expired', { category: 'data' });
|
|
54
57
|
}
|
|
@@ -76,14 +79,15 @@ export class CacheService {
|
|
|
76
79
|
async set(id: string, keySpace: string, entry: unknown, maxAge?: number): Promise<unknown> {
|
|
77
80
|
const entryText = JSONUtil.toBase64(entry);
|
|
78
81
|
|
|
79
|
-
const store = await this.#modelService.upsert(
|
|
82
|
+
const store = await this.#modelService.upsert(
|
|
83
|
+
CacheRecord,
|
|
80
84
|
CacheRecord.from({
|
|
81
85
|
id,
|
|
82
86
|
entry: entryText!,
|
|
83
87
|
keySpace,
|
|
84
88
|
expiresAt: TimeUtil.fromNow(maxAge || INFINITE_MAX_AGE),
|
|
85
89
|
issuedAt: new Date()
|
|
86
|
-
})
|
|
90
|
+
})
|
|
87
91
|
);
|
|
88
92
|
|
|
89
93
|
return JSONUtil.fromBase64(store.entry);
|
|
@@ -104,8 +108,10 @@ export class CacheService {
|
|
|
104
108
|
async deleteAll(keySpace: string): Promise<void> {
|
|
105
109
|
if (ModelIndexedUtil.isSupported(this.#modelService)) {
|
|
106
110
|
const removes: Promise<void>[] = [];
|
|
107
|
-
for await (const
|
|
108
|
-
|
|
111
|
+
for await (const batch of this.#modelService.listByIndex(CacheRecord, keySpaceIndex, { keySpace })) {
|
|
112
|
+
for (const item of batch) {
|
|
113
|
+
removes.push(this.#modelService.delete(CacheRecord, item.id));
|
|
114
|
+
}
|
|
109
115
|
}
|
|
110
116
|
await Promise.all(removes);
|
|
111
117
|
} else {
|
|
@@ -162,7 +168,8 @@ export class CacheService {
|
|
|
162
168
|
result = await this.set(id, config.keySpace!, data, config.maxAge);
|
|
163
169
|
}
|
|
164
170
|
|
|
165
|
-
if (config.reinstate) {
|
|
171
|
+
if (config.reinstate) {
|
|
172
|
+
// Reinstate result value if needed
|
|
166
173
|
result = config.reinstate(result);
|
|
167
174
|
}
|
|
168
175
|
|
|
@@ -190,4 +197,4 @@ export class CacheService {
|
|
|
190
197
|
}
|
|
191
198
|
return result;
|
|
192
199
|
}
|
|
193
|
-
}
|
|
200
|
+
}
|
package/src/types.ts
CHANGED
package/src/util.ts
CHANGED
|
@@ -6,7 +6,6 @@ import type { CoreCacheConfig } from './types.ts';
|
|
|
6
6
|
* Standard cache utilities
|
|
7
7
|
*/
|
|
8
8
|
export class CacheUtil {
|
|
9
|
-
|
|
10
9
|
/**
|
|
11
10
|
* Generate key given config, cache source and input params
|
|
12
11
|
*/
|
|
@@ -16,4 +15,4 @@ export class CacheUtil {
|
|
|
16
15
|
const key = `${config.keySpace!}_${JSONUtil.toBase64(keyParams)}`;
|
|
17
16
|
return BinaryMetadataUtil.hash(key, { length: 32 });
|
|
18
17
|
}
|
|
19
|
-
}
|
|
18
|
+
}
|
package/support/test/service.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import assert from 'node:assert';
|
|
2
2
|
import timers from 'node:timers/promises';
|
|
3
3
|
|
|
4
|
-
import { Suite, Test } from '@travetto/test';
|
|
5
|
-
import { type ModelExpirySupport, ModelIndexedUtil } from '@travetto/model';
|
|
6
4
|
import { Inject, Injectable } from '@travetto/di';
|
|
7
|
-
import {
|
|
5
|
+
import type { ModelExpirySupport } from '@travetto/model';
|
|
6
|
+
import { ModelIndexedUtil } from '@travetto/model-indexed';
|
|
7
|
+
import { type Class, castTo } from '@travetto/runtime';
|
|
8
8
|
import { Schema } from '@travetto/schema';
|
|
9
|
+
import { Suite, Test } from '@travetto/test';
|
|
9
10
|
|
|
10
11
|
import { InjectableSuite } from '@travetto/di/support/test/suite.ts';
|
|
11
12
|
import { ModelSuite } from '@travetto/model/support/test/suite.ts';
|
|
@@ -15,11 +16,10 @@ import type { CacheService } from '../../src/service.ts';
|
|
|
15
16
|
import { CacheModelSymbol } from '../../src/types.ts';
|
|
16
17
|
|
|
17
18
|
@Schema()
|
|
18
|
-
class User {
|
|
19
|
+
class User {}
|
|
19
20
|
|
|
20
21
|
@Injectable()
|
|
21
22
|
class SampleService {
|
|
22
|
-
|
|
23
23
|
@Inject()
|
|
24
24
|
source: CacheService;
|
|
25
25
|
|
|
@@ -85,7 +85,6 @@ class SampleService {
|
|
|
85
85
|
@ModelSuite(CacheModelSymbol)
|
|
86
86
|
@InjectableSuite()
|
|
87
87
|
export abstract class CacheServiceSuite {
|
|
88
|
-
|
|
89
88
|
serviceClass: Class<ModelExpirySupport>;
|
|
90
89
|
configClass: Class;
|
|
91
90
|
|
|
@@ -106,7 +105,7 @@ export abstract class CacheServiceSuite {
|
|
|
106
105
|
start = Date.now();
|
|
107
106
|
res = await service.basic(10);
|
|
108
107
|
diff = Date.now() - start;
|
|
109
|
-
assert(diff <
|
|
108
|
+
assert(diff < 100 + this.baseLatency);
|
|
110
109
|
assert(res === 20);
|
|
111
110
|
}
|
|
112
111
|
|
|
@@ -145,7 +144,7 @@ export abstract class CacheServiceSuite {
|
|
|
145
144
|
start = Date.now();
|
|
146
145
|
res = await service.ageExtension(10);
|
|
147
146
|
diff = Date.now() - start;
|
|
148
|
-
assert(diff <
|
|
147
|
+
assert(diff < 100 + this.baseLatency);
|
|
149
148
|
assert(res === 30);
|
|
150
149
|
}
|
|
151
150
|
|
|
@@ -202,12 +201,12 @@ export abstract class CacheServiceSuite {
|
|
|
202
201
|
await service.getUser('200');
|
|
203
202
|
const start = Date.now();
|
|
204
203
|
await service.getUser('200');
|
|
205
|
-
assert(
|
|
204
|
+
assert(Date.now() - start <= this.baseLatency + 100);
|
|
206
205
|
|
|
207
206
|
await service.deleteUser('200');
|
|
208
207
|
const start2 = Date.now();
|
|
209
208
|
await service.getUser('200');
|
|
210
|
-
assert(
|
|
209
|
+
assert(Date.now() - start2 >= 100);
|
|
211
210
|
|
|
212
211
|
// First time is free
|
|
213
212
|
await service.deleteUser('200');
|
|
@@ -224,33 +223,33 @@ export abstract class CacheServiceSuite {
|
|
|
224
223
|
const service = this.testService;
|
|
225
224
|
|
|
226
225
|
// Prime cache
|
|
227
|
-
for (let i = 0; i < 10; i
|
|
226
|
+
for (let i = 0; i < 10; i += 1) {
|
|
228
227
|
const start = Date.now();
|
|
229
228
|
await service.getUser(`${i}`);
|
|
230
|
-
assert(
|
|
229
|
+
assert(Date.now() - start >= 100);
|
|
231
230
|
}
|
|
232
231
|
|
|
233
232
|
// Read cache
|
|
234
|
-
for (let i = 0; i < 10; i
|
|
233
|
+
for (let i = 0; i < 10; i += 1) {
|
|
235
234
|
const start = Date.now();
|
|
236
235
|
await service.getUser(`${i}`);
|
|
237
|
-
assert(
|
|
236
|
+
assert(Date.now() - start <= this.baseLatency + 100);
|
|
238
237
|
}
|
|
239
238
|
|
|
240
239
|
await service.deleteAllUsers();
|
|
241
240
|
|
|
242
241
|
// Prime cache
|
|
243
|
-
for (let i = 0; i < 10; i
|
|
242
|
+
for (let i = 0; i < 10; i += 1) {
|
|
244
243
|
const start = Date.now();
|
|
245
244
|
await service.getUser(`${i}`);
|
|
246
|
-
assert(
|
|
245
|
+
assert(Date.now() - start >= 100);
|
|
247
246
|
}
|
|
248
247
|
|
|
249
248
|
// Read cache
|
|
250
|
-
for (let i = 0; i < 10; i
|
|
249
|
+
for (let i = 0; i < 10; i += 1) {
|
|
251
250
|
const start = Date.now();
|
|
252
251
|
await service.getUser(`${i}`);
|
|
253
|
-
assert(
|
|
252
|
+
assert(Date.now() - start <= this.baseLatency + 100);
|
|
254
253
|
}
|
|
255
254
|
}
|
|
256
|
-
}
|
|
255
|
+
}
|