@travetto/cache 3.0.0-rc.1 → 3.0.0-rc.10
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 +16 -14
- package/{index.ts → __index__.ts} +0 -0
- package/package.json +20 -16
- package/src/decorator.ts +4 -5
- package/src/internal/types.ts +2 -2
- package/src/service.ts +3 -3
- package/src/util.ts +3 -3
- package/{test-support → support/test}/service.ts +19 -17
- package/support/transformer.cache.ts +2 -4
package/README.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<!-- This file was generated by @travetto/doc and should not be modified directly -->
|
|
2
|
-
<!-- Please modify https://github.com/travetto/travetto/tree/main/module/cache/
|
|
2
|
+
<!-- Please modify https://github.com/travetto/travetto/tree/main/module/cache/DOC.ts and execute "npx trv doc" to rebuild -->
|
|
3
3
|
# Caching
|
|
4
4
|
## Caching functionality with decorators for declarative use.
|
|
5
5
|
|
|
@@ -19,20 +19,22 @@ npm install @travetto/model-{provider}
|
|
|
19
19
|
|
|
20
20
|
Currently, the following are packages that provide [Expiry](https://github.com/travetto/travetto/tree/main/module/model/src/service/expiry.ts#L11):
|
|
21
21
|
|
|
22
|
-
* [Data Modeling Support](https://github.com/travetto/travetto/tree/main/module/model#readme "Datastore abstraction for core operations.") - [FileModelService](https://github.com/travetto/travetto/tree/main/module/model/src/provider/file.ts#
|
|
23
|
-
* [DynamoDB Model Support](https://github.com/travetto/travetto/tree/main/module/model-dynamodb#readme "DynamoDB backing for the travetto model module.") -
|
|
24
|
-
* [Elasticsearch Model Source](https://github.com/travetto/travetto/tree/main/module/model-elasticsearch#readme "Elasticsearch backing for the travetto model module, with real-time modeling support for Elasticsearch mappings.") -
|
|
25
|
-
* [MongoDB Model Support](https://github.com/travetto/travetto/tree/main/module/model-mongo#readme "Mongo backing for the travetto model module.") -
|
|
26
|
-
* [Redis Model Support](https://github.com/travetto/travetto/tree/main/module/model-redis#readme "Redis backing for the travetto model module.") -
|
|
27
|
-
* [S3 Model Support](https://github.com/travetto/travetto/tree/main/module/model-s3#readme "S3 backing for the travetto model module.") -
|
|
28
|
-
* [
|
|
22
|
+
* [Data Modeling Support](https://github.com/travetto/travetto/tree/main/module/model#readme "Datastore abstraction for core operations.") - @travetto/model: [FileModelService](https://github.com/travetto/travetto/tree/main/module/model/src/provider/file.ts#L51), [MemoryModelService](https://github.com/travetto/travetto/tree/main/module/model/src/provider/memory.ts#L54)
|
|
23
|
+
* [DynamoDB Model Support](https://github.com/travetto/travetto/tree/main/module/model-dynamodb#readme "DynamoDB backing for the travetto model module.") - @travetto/model-dynamodb
|
|
24
|
+
* [Elasticsearch Model Source](https://github.com/travetto/travetto/tree/main/module/model-elasticsearch#readme "Elasticsearch backing for the travetto model module, with real-time modeling support for Elasticsearch mappings.") - @travetto/model-elasticsearch
|
|
25
|
+
* [MongoDB Model Support](https://github.com/travetto/travetto/tree/main/module/model-mongo#readme "Mongo backing for the travetto model module.") - @travetto/model-mongo
|
|
26
|
+
* [Redis Model Support](https://github.com/travetto/travetto/tree/main/module/model-redis#readme "Redis backing for the travetto model module.") - @travetto/model-redis
|
|
27
|
+
* [S3 Model Support](https://github.com/travetto/travetto/tree/main/module/model-s3#readme "S3 backing for the travetto model module.") - @travetto/model-s3
|
|
28
|
+
* [PostgreSQL Model Service](https://github.com/travetto/travetto/tree/main/module/model-postgres#readme "PostgreSQL backing for the travetto model module, with real-time modeling support for SQL schemas.") - @travetto/model-postgres
|
|
29
|
+
* [MySQL Model Service](https://github.com/travetto/travetto/tree/main/module/model-mysql#readme "MySQL backing for the travetto model module, with real-time modeling support for SQL schemas.") - @travetto/model-mysql
|
|
30
|
+
* [SQLite Model Service](https://github.com/travetto/travetto/tree/main/module/model-sqlite#readme "SQLite backing for the travetto model module, with real-time modeling support for SQL schemas.") - @travetto/model-sqlite
|
|
29
31
|
|
|
30
32
|
## Decorators
|
|
31
33
|
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.
|
|
32
34
|
|
|
33
35
|
Additionally, to use the decorators you will need to have a [CacheService](https://github.com/travetto/travetto/tree/main/module/cache/src/service.ts#L29) 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.
|
|
34
36
|
|
|
35
|
-
[@Cache](https://github.com/travetto/travetto/tree/main/module/cache/src/decorator.ts#
|
|
37
|
+
[@Cache](https://github.com/travetto/travetto/tree/main/module/cache/src/decorator.ts#L13) 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.
|
|
36
38
|
|
|
37
39
|
**Code: Using decorators to cache expensive async call**
|
|
38
40
|
```typescript
|
|
@@ -59,9 +61,9 @@ export class Worker {
|
|
|
59
61
|
}
|
|
60
62
|
```
|
|
61
63
|
|
|
62
|
-
### [@Cache](https://github.com/travetto/travetto/tree/main/module/cache/src/decorator.ts#
|
|
64
|
+
### [@Cache](https://github.com/travetto/travetto/tree/main/module/cache/src/decorator.ts#L13)
|
|
63
65
|
|
|
64
|
-
The [@Cache](https://github.com/travetto/travetto/tree/main/module/cache/src/decorator.ts#
|
|
66
|
+
The [@Cache](https://github.com/travetto/travetto/tree/main/module/cache/src/decorator.ts#L13) decorator supports configurations on:
|
|
65
67
|
|
|
66
68
|
|
|
67
69
|
* `name` the field name of the current class which points to the desired cache source.
|
|
@@ -74,9 +76,9 @@ The [@Cache](https://github.com/travetto/travetto/tree/main/module/cache/src/dec
|
|
|
74
76
|
* `serialize` the function to execute before storing a cacheable value. This allows for any custom data modification needed to persist as a string properly.
|
|
75
77
|
* `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.
|
|
76
78
|
|
|
77
|
-
### [@EvictCache](https://github.com/travetto/travetto/tree/main/module/cache/src/decorator.ts#
|
|
79
|
+
### [@EvictCache](https://github.com/travetto/travetto/tree/main/module/cache/src/decorator.ts#L40)
|
|
78
80
|
|
|
79
|
-
Additionally, there is support for planned eviction via the [@EvictCache](https://github.com/travetto/travetto/tree/main/module/cache/src/decorator.ts#
|
|
81
|
+
Additionally, there is support for planned eviction via the [@EvictCache](https://github.com/travetto/travetto/tree/main/module/cache/src/decorator.ts#L40) 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.
|
|
80
82
|
|
|
81
83
|
**Code: Using decorators to cache/evict user access**
|
|
82
84
|
```typescript
|
|
@@ -113,7 +115,7 @@ export class UserService {
|
|
|
113
115
|
|
|
114
116
|
## Extending the Cache Service
|
|
115
117
|
|
|
116
|
-
By design, the [CacheService](https://github.com/travetto/travetto/tree/main/module/cache/src/service.ts#L29) 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/service/expiry.ts#L11). This combines basic support for CRUD as well as knowledge of how to manage expirable content. 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#L29). The [CacheService](https://github.com/travetto/travetto/tree/main/module/cache/src/service.ts#L29) is expecting the model service to be registered using the @
|
|
118
|
+
By design, the [CacheService](https://github.com/travetto/travetto/tree/main/module/cache/src/service.ts#L29) 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/service/expiry.ts#L11). This combines basic support for CRUD as well as knowledge of how to manage expirable content. 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#L29). The [CacheService](https://github.com/travetto/travetto/tree/main/module/cache/src/service.ts#L29) is expecting the model service to be registered using the @travetto/cache:model:
|
|
117
119
|
|
|
118
120
|
**Code: Registering a Custom Model Source**
|
|
119
121
|
```typescript
|
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/cache",
|
|
3
|
-
"
|
|
4
|
-
"version": "3.0.0-rc.1",
|
|
3
|
+
"version": "3.0.0-rc.10",
|
|
5
4
|
"description": "Caching functionality with decorators for declarative use.",
|
|
6
5
|
"keywords": [
|
|
7
6
|
"typescript",
|
|
@@ -16,28 +15,33 @@
|
|
|
16
15
|
"name": "Travetto Framework"
|
|
17
16
|
},
|
|
18
17
|
"files": [
|
|
19
|
-
"
|
|
18
|
+
"__index__.ts",
|
|
20
19
|
"src",
|
|
21
|
-
"support"
|
|
22
|
-
"test-support"
|
|
20
|
+
"support"
|
|
23
21
|
],
|
|
24
|
-
"main": "
|
|
22
|
+
"main": "__index__.ts",
|
|
25
23
|
"repository": {
|
|
26
24
|
"url": "https://github.com/travetto/travetto.git",
|
|
27
25
|
"directory": "module/cache"
|
|
28
26
|
},
|
|
29
27
|
"dependencies": {
|
|
30
|
-
"@travetto/
|
|
31
|
-
"@travetto/
|
|
32
|
-
"@travetto/model": "^3.0.0-rc.1"
|
|
28
|
+
"@travetto/di": "^3.0.0-rc.10",
|
|
29
|
+
"@travetto/model": "^3.0.0-rc.10"
|
|
33
30
|
},
|
|
34
|
-
"
|
|
35
|
-
"@travetto/
|
|
36
|
-
"@travetto/
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
"@travetto/
|
|
40
|
-
|
|
31
|
+
"peerDependencies": {
|
|
32
|
+
"@travetto/test": "^3.0.0-rc.11",
|
|
33
|
+
"@travetto/transformer": "^3.0.0-rc.9"
|
|
34
|
+
},
|
|
35
|
+
"peerDependenciesMeta": {
|
|
36
|
+
"@travetto/transformer": {
|
|
37
|
+
"optional": true
|
|
38
|
+
},
|
|
39
|
+
"@travetto/test": {
|
|
40
|
+
"optional": true
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
"travetto": {
|
|
44
|
+
"displayName": "Caching"
|
|
41
45
|
},
|
|
42
46
|
"publishConfig": {
|
|
43
47
|
"access": "public"
|
package/src/decorator.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { MethodDescriptor } from '@travetto/base
|
|
2
|
-
import { TimeSpan, Util } from '@travetto/base';
|
|
1
|
+
import { MethodDescriptor, TimeSpan, TimeUtil } from '@travetto/base';
|
|
3
2
|
|
|
4
3
|
import { CacheService } from './service';
|
|
5
4
|
import { CoreCacheConfig, CacheConfig } from './types';
|
|
@@ -9,7 +8,7 @@ import { CacheAware, CacheConfigⲐ, EvictConfigⲐ } from './internal/types';
|
|
|
9
8
|
* Indicates a method is intended to cache. The return type must be properly serializable
|
|
10
9
|
* @param field The field of the cache source
|
|
11
10
|
* @param config The additional cache configuration
|
|
12
|
-
* @augments `@
|
|
11
|
+
* @augments `@travetto/cache:Cache`
|
|
13
12
|
*/
|
|
14
13
|
export function Cache<F extends string, U extends Record<F, CacheService>>(field: F, maxAge: number | TimeSpan, config?: Omit<CacheConfig, 'maxAge'>): MethodDecorator;
|
|
15
14
|
export function Cache<F extends string, U extends Record<F, CacheService>>(field: F, cfg?: CacheConfig): MethodDecorator;
|
|
@@ -18,7 +17,7 @@ export function Cache<F extends string, U extends Record<F, CacheService>>(
|
|
|
18
17
|
): MethodDecorator {
|
|
19
18
|
if (cfg !== undefined) {
|
|
20
19
|
if (typeof cfg === 'string' || typeof cfg === 'number') {
|
|
21
|
-
config.maxAge =
|
|
20
|
+
config.maxAge = TimeUtil.timeToMs(cfg);
|
|
22
21
|
} else {
|
|
23
22
|
config = cfg;
|
|
24
23
|
}
|
|
@@ -36,7 +35,7 @@ export function Cache<F extends string, U extends Record<F, CacheService>>(
|
|
|
36
35
|
* freshest data will be collected
|
|
37
36
|
* @param field The field of the cache source
|
|
38
37
|
* @param config The additional cache configuration
|
|
39
|
-
* @augments `@
|
|
38
|
+
* @augments `@travetto/cache:Evict`
|
|
40
39
|
*/
|
|
41
40
|
export function EvictCache<F extends string, U extends Record<F, CacheService>>(field: F, config: CoreCacheConfig = {}) {
|
|
42
41
|
return function <R extends Promise<unknown>>(target: U & CacheAware, propertyKey: string, descriptor: MethodDescriptor<R>): void {
|
package/src/internal/types.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CacheConfig, CoreCacheConfig } from '../types';
|
|
2
2
|
|
|
3
|
-
export const CacheConfigⲐ = Symbol.for('@
|
|
4
|
-
export const EvictConfigⲐ = Symbol.for('@
|
|
3
|
+
export const CacheConfigⲐ = Symbol.for('@travetto/cache:cache');
|
|
4
|
+
export const EvictConfigⲐ = Symbol.for('@travetto/cache:evict');
|
|
5
5
|
|
|
6
6
|
export interface CacheAware {
|
|
7
7
|
[CacheConfigⲐ]?: Record<string, CacheConfig>;
|
package/src/service.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { ExpiresAt, Model, ModelExpirySupport, NotFoundError } from '@travetto/model';
|
|
2
2
|
import { Text } from '@travetto/schema';
|
|
3
3
|
import { Inject, Injectable } from '@travetto/di';
|
|
4
|
-
import {
|
|
4
|
+
import { GlobalEnv } from '@travetto/base';
|
|
5
5
|
import { isStorageSupported } from '@travetto/model/src/internal/service/common';
|
|
6
6
|
|
|
7
7
|
import { CacheError } from './error';
|
|
8
8
|
import { CacheUtil } from './util';
|
|
9
9
|
import { CacheAware, CacheConfigⲐ, EvictConfigⲐ } from './internal/types';
|
|
10
10
|
|
|
11
|
-
export const CacheModelⲐ = Symbol.for('@
|
|
11
|
+
export const CacheModelⲐ = Symbol.for('@travetto/cache:model');
|
|
12
12
|
|
|
13
13
|
const INFINITE_MAX_AGE = '5000-01-01';
|
|
14
14
|
|
|
@@ -35,7 +35,7 @@ export class CacheService {
|
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
async postConstruct(): Promise<void> {
|
|
38
|
-
if (isStorageSupported(this.#modelService) &&
|
|
38
|
+
if (isStorageSupported(this.#modelService) && GlobalEnv.dynamic) {
|
|
39
39
|
await this.#modelService.createModel?.(CacheRecord);
|
|
40
40
|
}
|
|
41
41
|
}
|
package/src/util.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import crypto from 'crypto';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { ObjectUtil } from '@travetto/base';
|
|
4
4
|
import { CoreCacheConfig } from './types';
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -15,7 +15,7 @@ export class CacheUtil {
|
|
|
15
15
|
*/
|
|
16
16
|
static toSafeJSON(value: unknown, all = false): string {
|
|
17
17
|
const replacer = all ?
|
|
18
|
-
((key: string, val: unknown): unknown => (val && val instanceof RegExp) ? val.source : (
|
|
18
|
+
((key: string, val: unknown): unknown => (val && val instanceof RegExp) ? val.source : (ObjectUtil.isFunction(val) ? val.toString() : val)) :
|
|
19
19
|
undefined;
|
|
20
20
|
|
|
21
21
|
return Buffer.from(JSON.stringify(value, replacer)).toString('base64');
|
|
@@ -1,16 +1,18 @@
|
|
|
1
|
-
import
|
|
1
|
+
import assert from 'assert';
|
|
2
2
|
|
|
3
3
|
import { Suite, Test } from '@travetto/test';
|
|
4
4
|
import { ModelExpirySupport } from '@travetto/model';
|
|
5
5
|
import { Inject, Injectable } from '@travetto/di';
|
|
6
|
-
import { InjectableSuite } from '@travetto/di/
|
|
7
|
-
import { ModelSuite } from '@travetto/model/
|
|
8
|
-
import { Class,
|
|
6
|
+
import { InjectableSuite } from '@travetto/di/support/test/suite';
|
|
7
|
+
import { ModelSuite } from '@travetto/model/support/test/suite';
|
|
8
|
+
import { Class, TimeUtil } from '@travetto/base';
|
|
9
|
+
import { Schema } from '@travetto/schema';
|
|
9
10
|
|
|
10
|
-
import { Cache, EvictCache } from '
|
|
11
|
-
import { CacheModelⲐ, CacheService } from '
|
|
12
|
-
import { CacheUtil } from '
|
|
11
|
+
import { Cache, EvictCache } from '../../src/decorator';
|
|
12
|
+
import { CacheModelⲐ, CacheService } from '../../src/service';
|
|
13
|
+
import { CacheUtil } from '../../src/util';
|
|
13
14
|
|
|
15
|
+
@Schema()
|
|
14
16
|
class User { }
|
|
15
17
|
|
|
16
18
|
@Injectable()
|
|
@@ -26,37 +28,37 @@ class SampleService {
|
|
|
26
28
|
|
|
27
29
|
@Cache('source')
|
|
28
30
|
async basic(num: number) {
|
|
29
|
-
await
|
|
31
|
+
await TimeUtil.wait(100);
|
|
30
32
|
return num * 2;
|
|
31
33
|
}
|
|
32
34
|
|
|
33
35
|
@Cache('source', '.5s')
|
|
34
36
|
async agesQuickly(num: number) {
|
|
35
|
-
await
|
|
37
|
+
await TimeUtil.wait(100);
|
|
36
38
|
return num * 3;
|
|
37
39
|
}
|
|
38
40
|
|
|
39
41
|
@Cache('source', 200, { extendOnAccess: true })
|
|
40
42
|
async ageExtension(num: number) {
|
|
41
|
-
await
|
|
43
|
+
await TimeUtil.wait(100);
|
|
42
44
|
return num * 3;
|
|
43
45
|
}
|
|
44
46
|
|
|
45
47
|
@Cache('source')
|
|
46
48
|
async complexInput(config: object, size: number) {
|
|
47
|
-
await
|
|
49
|
+
await TimeUtil.wait(100);
|
|
48
50
|
return { length: Object.keys(config).length, size };
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
@Cache('source', { key: config => config.a })
|
|
52
54
|
async customKey(config: object, size: number) {
|
|
53
|
-
await
|
|
55
|
+
await TimeUtil.wait(100);
|
|
54
56
|
return { length: Object.keys(config).length, size };
|
|
55
57
|
}
|
|
56
58
|
|
|
57
59
|
@Cache('source', { keySpace: 'user.id', reinstate: x => User.from(x as User) })
|
|
58
60
|
async getUser(userId: string) {
|
|
59
|
-
await
|
|
61
|
+
await TimeUtil.wait(100);
|
|
60
62
|
|
|
61
63
|
return {
|
|
62
64
|
id: userId,
|
|
@@ -66,7 +68,7 @@ class SampleService {
|
|
|
66
68
|
|
|
67
69
|
@EvictCache('source', { keySpace: 'user.id' })
|
|
68
70
|
async deleteUser(userId: string) {
|
|
69
|
-
await
|
|
71
|
+
await TimeUtil.wait(100);
|
|
70
72
|
return true;
|
|
71
73
|
}
|
|
72
74
|
}
|
|
@@ -110,7 +112,7 @@ export abstract class CacheServiceSuite {
|
|
|
110
112
|
assert(diff > 75);
|
|
111
113
|
assert(res === 30);
|
|
112
114
|
|
|
113
|
-
await
|
|
115
|
+
await TimeUtil.wait(510);
|
|
114
116
|
|
|
115
117
|
start = Date.now();
|
|
116
118
|
res = await service.agesQuickly(10);
|
|
@@ -130,7 +132,7 @@ export abstract class CacheServiceSuite {
|
|
|
130
132
|
assert(res === 30);
|
|
131
133
|
|
|
132
134
|
for (let i = 0; i < 2; i += 1) {
|
|
133
|
-
await
|
|
135
|
+
await TimeUtil.wait(55);
|
|
134
136
|
|
|
135
137
|
start = Date.now();
|
|
136
138
|
res = await service.ageExtension(10);
|
|
@@ -139,7 +141,7 @@ export abstract class CacheServiceSuite {
|
|
|
139
141
|
assert(res === 30);
|
|
140
142
|
}
|
|
141
143
|
|
|
142
|
-
await
|
|
144
|
+
await TimeUtil.wait(210);
|
|
143
145
|
start = Date.now();
|
|
144
146
|
res = await service.ageExtension(10);
|
|
145
147
|
diff = Date.now() - start;
|
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
import
|
|
1
|
+
import ts from 'typescript';
|
|
2
2
|
|
|
3
|
-
import { TransformerState, DecoratorMeta, OnMethod
|
|
3
|
+
import { TransformerState, DecoratorMeta, OnMethod } from '@travetto/transformer';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Transform the cache headers
|
|
7
7
|
*/
|
|
8
8
|
export class CacheTransformer {
|
|
9
9
|
|
|
10
|
-
static [TransformerId] = '@trv:cache';
|
|
11
|
-
|
|
12
10
|
/**
|
|
13
11
|
* When `@Cache` and `@Evict` are present
|
|
14
12
|
*/
|