@jintianxiayu/cache-decorator 1.0.0 → 1.0.2
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/CHANGELOG.md +25 -11
- package/README.md +364 -270
- package/dist/core/cache-error.d.ts +75 -0
- package/dist/core/cache-error.d.ts.map +1 -0
- package/dist/core/cache-error.js +168 -0
- package/dist/core/cache-error.js.map +1 -0
- package/dist/core/cache-logger.d.ts +3 -2
- package/dist/core/cache-logger.d.ts.map +1 -1
- package/dist/core/cache-logger.js +2 -0
- package/dist/core/cache-logger.js.map +1 -1
- package/dist/decorators/cache-evict.d.ts.map +1 -1
- package/dist/decorators/cache-evict.js +15 -10
- package/dist/decorators/cache-evict.js.map +1 -1
- package/dist/decorators/cache.d.ts +6 -0
- package/dist/decorators/cache.d.ts.map +1 -1
- package/dist/decorators/cache.js +113 -36
- package/dist/decorators/cache.js.map +1 -1
- package/jest.config.js +11 -11
- package/package.json +1 -1
- package/src/adapters/ioredis-cache-client.ts +89 -89
- package/src/adapters/node-redis-cache-client.ts +95 -95
- package/src/adapters/redis-key-prefix.ts +38 -38
- package/src/core/cache-error.ts +233 -0
- package/src/core/cache-logger.ts +116 -105
- package/src/core/key-builder.ts +33 -33
- package/src/core/native-cache.ts +55 -55
- package/src/core/pending-cache.ts +29 -29
- package/src/core/redis-cache-client.ts +160 -160
- package/src/core/redis-cache.ts +104 -104
- package/src/decorators/cache-evict.ts +137 -129
- package/src/decorators/cache.ts +323 -203
- package/src/index.ts +11 -11
- package/test/cache-evict-logging.test.ts +398 -362
- package/test/cache-logger.integration.test.ts +159 -129
- package/test/cache-logger.test.ts +156 -153
- package/test/cache-logging.test.ts +929 -544
- package/test/cache.test.ts +1017 -255
- package/test/fixtures/cache-logger-child.mjs +108 -108
- package/test/fixtures/cache-provider-failure-child.mjs +70 -0
- package/test/helpers/legacy-redis-cache.ts +41 -22
- package/test/helpers/package-consumer.ts +231 -231
- package/test/helpers/redis-fixture.ts +142 -142
- package/test/ioredis-cache-client.test.ts +143 -143
- package/test/legacy-redis-cache.test.ts +51 -0
- package/test/native-cache.test.ts +77 -77
- package/test/node-redis-cache-client.test.ts +149 -149
- package/test/pending-cache.test.ts +69 -69
- package/test/redis-cache-client-lifecycle.test.ts +112 -112
- package/test/redis-cache-client-types.test.ts +184 -184
- package/test/redis-cache-client.integration.test.ts +355 -327
- package/test/redis-cache-decorator.test.ts +601 -201
- package/test/redis-cache-provider.test.ts +269 -269
- package/test/type-contract/contract.ts +119 -64
- package/test/type-contract/tsconfig.json +12 -12
- package/tsconfig.json +8 -8
|
@@ -1,129 +1,137 @@
|
|
|
1
|
-
import { cacheProviderLabel, logCacheEvent, type CacheLogContext } from '../core/cache-logger';
|
|
2
|
-
import type { CacheProvider } from '../core/cache-provider';
|
|
3
|
-
import { CacheProviderRegistry } from '../core/cache-provider-registry';
|
|
4
|
-
import { KeyBuilder } from '../core/key-builder';
|
|
5
|
-
import type { CacheKeyResolver, CacheOptions } from './cache';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* @CacheEvict 装饰器配置项
|
|
9
|
-
*/
|
|
10
|
-
export interface CacheEvictOptions extends Pick<CacheOptions, 'key'> {
|
|
11
|
-
/**
|
|
12
|
-
* 是否清除所有条目,默认为 false
|
|
13
|
-
*/
|
|
14
|
-
allEntries?: boolean;
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* 指定 CacheProvider 名称
|
|
18
|
-
*/
|
|
19
|
-
providerName?: string;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* 解析缓存 key
|
|
24
|
-
* @param keyResolver key 解析器
|
|
25
|
-
* @param args 方法参数数组
|
|
26
|
-
* @param logContext 不包含业务参数和值的日志上下文
|
|
27
|
-
* @returns 解析后的缓存 key
|
|
28
|
-
*/
|
|
29
|
-
function resolveCacheKey(
|
|
30
|
-
keyResolver: CacheKeyResolver | undefined,
|
|
31
|
-
args: unknown[],
|
|
32
|
-
logContext: CacheLogContext
|
|
33
|
-
): string {
|
|
34
|
-
if (keyResolver === undefined || keyResolver === null) {
|
|
35
|
-
return KeyBuilder.build(logContext.cacheName, args);
|
|
36
|
-
}
|
|
37
|
-
if (typeof keyResolver === 'string') {
|
|
38
|
-
return KeyBuilder.build(logContext.cacheName, [keyResolver]);
|
|
39
|
-
}
|
|
40
|
-
try {
|
|
41
|
-
return KeyBuilder.build(logContext.cacheName, [keyResolver(...args)]);
|
|
42
|
-
} catch (_error) {
|
|
43
|
-
logCacheEvent('cache.key_fallback', { ...logContext, reason: 'resolver_error' });
|
|
44
|
-
return KeyBuilder.build(logContext.cacheName, args);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* 获取淘汰操作使用的 Provider
|
|
50
|
-
* @param providerName decorator 配置中的 Provider 名称。
|
|
51
|
-
* @param logContext 当前方法的稳定日志上下文。
|
|
52
|
-
* @returns 已注册的缓存 Provider。
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
*
|
|
68
|
-
* @param
|
|
69
|
-
* @
|
|
70
|
-
* @
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
|
|
1
|
+
import { cacheProviderLabel, logCacheEvent, type CacheLogContext } from '../core/cache-logger';
|
|
2
|
+
import type { CacheProvider } from '../core/cache-provider';
|
|
3
|
+
import { CacheProviderRegistry } from '../core/cache-provider-registry';
|
|
4
|
+
import { KeyBuilder } from '../core/key-builder';
|
|
5
|
+
import type { CacheKeyResolver, CacheOptions } from './cache';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @CacheEvict 装饰器配置项
|
|
9
|
+
*/
|
|
10
|
+
export interface CacheEvictOptions extends Pick<CacheOptions, 'key'> {
|
|
11
|
+
/**
|
|
12
|
+
* 是否清除所有条目,默认为 false
|
|
13
|
+
*/
|
|
14
|
+
allEntries?: boolean;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* 指定 CacheProvider 名称
|
|
18
|
+
*/
|
|
19
|
+
providerName?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 解析缓存 key
|
|
24
|
+
* @param keyResolver key 解析器
|
|
25
|
+
* @param args 方法参数数组
|
|
26
|
+
* @param logContext 不包含业务参数和值的日志上下文
|
|
27
|
+
* @returns 解析后的缓存 key
|
|
28
|
+
*/
|
|
29
|
+
function resolveCacheKey(
|
|
30
|
+
keyResolver: CacheKeyResolver | undefined,
|
|
31
|
+
args: unknown[],
|
|
32
|
+
logContext: CacheLogContext
|
|
33
|
+
): string {
|
|
34
|
+
if (keyResolver === undefined || keyResolver === null) {
|
|
35
|
+
return KeyBuilder.build(logContext.cacheName, args);
|
|
36
|
+
}
|
|
37
|
+
if (typeof keyResolver === 'string') {
|
|
38
|
+
return KeyBuilder.build(logContext.cacheName, [keyResolver]);
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
return KeyBuilder.build(logContext.cacheName, [keyResolver(...args)]);
|
|
42
|
+
} catch (_error) {
|
|
43
|
+
logCacheEvent('cache.key_fallback', { ...logContext, reason: 'resolver_error' });
|
|
44
|
+
return KeyBuilder.build(logContext.cacheName, args);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* 获取淘汰操作使用的 Provider,并把解析失败转换为缓存旁路。
|
|
50
|
+
* @param providerName decorator 配置中的 Provider 名称。
|
|
51
|
+
* @param logContext 当前方法的稳定日志上下文。
|
|
52
|
+
* @returns 已注册的缓存 Provider;解析失败时返回 undefined。
|
|
53
|
+
*/
|
|
54
|
+
function resolveCacheProvider(
|
|
55
|
+
providerName: string | undefined,
|
|
56
|
+
logContext: CacheLogContext
|
|
57
|
+
): CacheProvider | undefined {
|
|
58
|
+
try {
|
|
59
|
+
return CacheProviderRegistry.get(providerName);
|
|
60
|
+
} catch (error) {
|
|
61
|
+
logCacheEvent('cache.operation_failed', { ...logContext, operation: 'provider_resolution', error });
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* 保持 fire-and-forget 语义发起单 key 删除,并消费同步或异步 Provider 失败。
|
|
68
|
+
* @param provider 当前淘汰使用的 Provider。
|
|
69
|
+
* @param cacheKey 待删除的完整 key;不会进入日志元数据。
|
|
70
|
+
* @param logContext 当前方法的稳定日志上下文。
|
|
71
|
+
* @returns 无返回值;删除失败仅记录日志,不影响业务结果。
|
|
72
|
+
*/
|
|
73
|
+
function dispatchCacheDelete(provider: CacheProvider, cacheKey: string, logContext: CacheLogContext): void {
|
|
74
|
+
let operation: void | Promise<void>;
|
|
75
|
+
try {
|
|
76
|
+
operation = provider.delete(cacheKey);
|
|
77
|
+
} catch (error) {
|
|
78
|
+
logCacheEvent('cache.operation_failed', { ...logContext, operation: 'evict', error });
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
logCacheEvent('cache.evict_dispatched', { ...logContext, scope: 'key' });
|
|
82
|
+
void Promise.resolve(operation).catch((error: unknown) => {
|
|
83
|
+
logCacheEvent('cache.operation_failed', { ...logContext, operation: 'evict', error });
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* 缓存清除装饰器
|
|
89
|
+
* 在方法执行后清除对应的缓存条目
|
|
90
|
+
* @param cacheName 缓存名称
|
|
91
|
+
* @param options 配置项
|
|
92
|
+
*/
|
|
93
|
+
export function CacheEvict(
|
|
94
|
+
cacheName: string,
|
|
95
|
+
options?: CacheEvictOptions
|
|
96
|
+
): (_target: object, _propertyKey: string, descriptor: PropertyDescriptor) => void {
|
|
97
|
+
return function (_target: object, propertyKey: string, descriptor: PropertyDescriptor) {
|
|
98
|
+
const originalMethod = descriptor.value;
|
|
99
|
+
const logContext: CacheLogContext = {
|
|
100
|
+
cacheName,
|
|
101
|
+
methodName: propertyKey,
|
|
102
|
+
providerName: cacheProviderLabel(options?.providerName),
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
descriptor.value = async function (...args: unknown[]) {
|
|
106
|
+
let result: unknown;
|
|
107
|
+
try {
|
|
108
|
+
result = await originalMethod.apply(this, args);
|
|
109
|
+
} catch (error) {
|
|
110
|
+
logCacheEvent('cache.evict_skipped', { ...logContext, reason: 'business_error' });
|
|
111
|
+
throw error;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const provider = resolveCacheProvider(options?.providerName, logContext);
|
|
115
|
+
if (provider === undefined) {
|
|
116
|
+
return result;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (options?.allEntries) {
|
|
120
|
+
try {
|
|
121
|
+
await provider.deleteByPattern(cacheName + '*');
|
|
122
|
+
} catch (error) {
|
|
123
|
+
logCacheEvent('cache.operation_failed', { ...logContext, operation: 'evict', error });
|
|
124
|
+
return result;
|
|
125
|
+
}
|
|
126
|
+
logCacheEvent('cache.evict_completed', { ...logContext, scope: 'allEntries' });
|
|
127
|
+
} else {
|
|
128
|
+
const cacheKey = resolveCacheKey(options?.key, args, logContext);
|
|
129
|
+
dispatchCacheDelete(provider, cacheKey, logContext);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return result;
|
|
133
|
+
};
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export { CacheProviderRegistry } from '../core/cache-provider-registry';
|