@jintianxiayu/cache-decorator 1.0.1 → 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 CHANGED
@@ -1,5 +1,13 @@
1
1
  # @jintianxiayu/cache-decorator
2
2
 
3
+ ## 1.0.2
4
+
5
+ ### Patch Changes
6
+
7
+ - 缓存装饰器固定改为 fail-open:Provider 解析、读取、写入或淘汰失败时保留业务成功值或原始业务异常,并消费非等待写入与单 key 删除的异步 rejection。直接调用 Provider 仍保持 fail-fast。
8
+
9
+ 这是维护者因公共 API 未调整而批准的 patch 版本例外,计划从 1.0.1 升级到 1.0.2;运行时错误语义不兼容,下游不得再依赖装饰器传播 Provider rejection。
10
+
3
11
  ## 1.0.1
4
12
 
5
13
  ### Patch Changes
package/README.md CHANGED
@@ -118,8 +118,8 @@ class UserService {
118
118
  - `errorCache` 省略时,业务异常不会持久化;相同 key 的执行中调用仍会复用同一个 pending Promise。
119
119
  - `errorCache.ttl` 必须是大于零的有限整数秒,不能继承正常结果的 `ttl`。非法值会在 legacy decorator 求值时抛出 `RangeError`。
120
120
  - `shouldCache` 省略时会接受所有业务异常;返回 `false` 或自身抛错时按 fail-closed 跳过写入,并继续抛出原业务异常。
121
- - Provider 解析/读取、key resolver、Logger 和 codec 的故障不会作为业务异常缓存。
122
- - 异常写入沿用 fire-and-forget 边界;同步可观察的 Provider 写入失败不会替换已经发生的业务异常。
121
+ - Provider 解析或读取失败会旁路本次全部缓存操作,既不会作为业务异常缓存,也不会阻断业务方法。
122
+ - 异常写入沿用 fire-and-forget 边界;Provider 同步抛错或异步拒绝都不会替换已经发生的业务异常。
123
123
 
124
124
  默认 codec 会把标准 `Error` 保存为只包含 `name` 和 `message` 的 JSON payload。缓存命中会创建新的 `Error`,不保留原对象身份、`stack`、自定义原型或任意自有属性。非 `Error` 抛出值必须能安全 JSON 往返;`undefined`、函数、symbol、循环引用和非有限数会跳过写入。
125
125
 
@@ -190,13 +190,19 @@ Logger。
190
190
  | `error` | `cache.operation_failed` | Provider 解析或可观察的读取、写入、淘汰操作失败 |
191
191
 
192
192
  `write_dispatched` 和 `evict_dispatched` 只描述调用已发起。为保持既有时序,decorator 不等待 `set()` 或单 key
193
- `delete()` 返回的 Promise,也不为日志附加 rejection handler;异步失败不会被描述为成功或完成。只有本来就会等待的全量淘汰可记录
194
- `evict_completed`。
193
+ `delete()` 返回的 Promise,但会消费其 rejection 并记录 `cache.operation_failed`,避免形成未处理的 Promise rejection。只有本来就会
194
+ 等待的全量淘汰可记录 `evict_completed`;全量淘汰失败不会记录 completed,也不会替换业务结果。
195
195
 
196
196
  每条日志只包含 `event`、`cacheName`、`methodName`、`providerName`,并按事件增加 `entryType`、`scope`、
197
197
  `reason`、`phase`、`operation` 或基础设施 `error`。日志不会包含方法参数、业务返回值、缓存值、业务异常内容、codec
198
198
  payload、策略回调错误或完整逻辑/物理 cache key,也不会为日志额外序列化这些值。Provider 错误和现有
199
- `LoggerContext` 的 `traceId` 继续由 Logger 统一规范化、脱敏和关联;cache 包不直接读写 LoggerContext。Logger 自身同步失败会被隔离,不会替换缓存结果、业务结果或原始 Provider 错误。
199
+ `LoggerContext` 的 `traceId` 继续由 Logger 统一规范化、脱敏和关联;cache 包不直接读写 LoggerContext。Logger 自身同步失败会被隔离,不会改变缓存命中结果或 Provider 故障后的业务旁路结果。
200
+
201
+ ## Provider 故障语义
202
+
203
+ `@Cache` 与 `@CacheEvict` 固定采用 fail-open:Provider 不存在,或缓存读取、写入、单 key 删除、全量删除同步抛错或异步拒绝时,装饰器记录 `cache.operation_failed`,但仍向调用方返回业务成功结果或传播原始业务异常。读取失败不会记录为正常 miss,也不会在本次调用中写入正常或异常条目;淘汰失败不会记录为 completed。该行为没有配置开关。
204
+
205
+ fail-open 只存在于装饰器编排边界。直接调用 `CacheProvider`、`RedisCacheProvider` 或 Redis adapter 时,原始异常仍会正常抛出或拒绝,便于基础设施代码显式处理。装饰器不会自动重试、设置超时、切换到 Memory 或其他 Provider,也不负责 Memory 容量、淘汰策略或进程内存治理。
200
206
 
201
207
  ## Redis
202
208
 
@@ -1 +1 @@
1
- {"version":3,"file":"cache-evict.d.ts","sourceRoot":"","sources":["../../src/decorators/cache-evict.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAoB,YAAY,EAAE,MAAM,SAAS,CAAC;AAE9D;;GAEG;AACH,MAAM,WAAW,iBAAkB,SAAQ,IAAI,CAAC,YAAY,EAAE,KAAK,CAAC;IAChE;;OAEG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACzB;AA8DD;;;;;GAKG;AACH,wBAAgB,UAAU,CACtB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,iBAAiB,GAC5B,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,kBAAkB,KAAK,IAAI,CAoCjF;AAED,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC"}
1
+ {"version":3,"file":"cache-evict.d.ts","sourceRoot":"","sources":["../../src/decorators/cache-evict.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAoB,YAAY,EAAE,MAAM,SAAS,CAAC;AAE9D;;GAEG;AACH,MAAM,WAAW,iBAAkB,SAAQ,IAAI,CAAC,YAAY,EAAE,KAAK,CAAC;IAChE;;OAEG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IAErB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACzB;AAmED;;;;;GAKG;AACH,wBAAgB,UAAU,CACtB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,iBAAiB,GAC5B,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,kBAAkB,KAAK,IAAI,CAuCjF;AAED,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC"}
@@ -28,11 +28,10 @@ function resolveCacheKey(keyResolver, args, logContext) {
28
28
  }
29
29
  }
30
30
  /**
31
- * 获取淘汰操作使用的 Provider,并在解析失败时记录原始注册表错误。
31
+ * 获取淘汰操作使用的 Provider,并把解析失败转换为缓存旁路。
32
32
  * @param providerName decorator 配置中的 Provider 名称。
33
33
  * @param logContext 当前方法的稳定日志上下文。
34
- * @returns 已注册的缓存 Provider。
35
- * @throws Provider 注册表抛出的原始错误。
34
+ * @returns 已注册的缓存 Provider;解析失败时返回 undefined
36
35
  */
37
36
  function resolveCacheProvider(providerName, logContext) {
38
37
  try {
@@ -40,26 +39,29 @@ function resolveCacheProvider(providerName, logContext) {
40
39
  }
41
40
  catch (error) {
42
41
  (0, cache_logger_1.logCacheEvent)('cache.operation_failed', { ...logContext, operation: 'provider_resolution', error });
43
- throw error;
42
+ return undefined;
44
43
  }
45
44
  }
46
45
  /**
47
- * 保持 fire-and-forget 语义发起单 key 删除,只处理调用当下可观察的同步失败。
46
+ * 保持 fire-and-forget 语义发起单 key 删除,并消费同步或异步 Provider 失败。
48
47
  * @param provider 当前淘汰使用的 Provider。
49
48
  * @param cacheKey 待删除的完整 key;不会进入日志元数据。
50
49
  * @param logContext 当前方法的稳定日志上下文。
51
- * @returns 无返回值;异步删除 Promise 不会被等待或消费。
52
- * @throws Provider delete 同步抛出的原始错误。
50
+ * @returns 无返回值;删除失败仅记录日志,不影响业务结果。
53
51
  */
54
52
  function dispatchCacheDelete(provider, cacheKey, logContext) {
53
+ let operation;
55
54
  try {
56
- provider.delete(cacheKey);
55
+ operation = provider.delete(cacheKey);
57
56
  }
58
57
  catch (error) {
59
58
  (0, cache_logger_1.logCacheEvent)('cache.operation_failed', { ...logContext, operation: 'evict', error });
60
- throw error;
59
+ return;
61
60
  }
62
61
  (0, cache_logger_1.logCacheEvent)('cache.evict_dispatched', { ...logContext, scope: 'key' });
62
+ void Promise.resolve(operation).catch((error) => {
63
+ (0, cache_logger_1.logCacheEvent)('cache.operation_failed', { ...logContext, operation: 'evict', error });
64
+ });
63
65
  }
64
66
  /**
65
67
  * 缓存清除装饰器
@@ -85,13 +87,16 @@ function CacheEvict(cacheName, options) {
85
87
  throw error;
86
88
  }
87
89
  const provider = resolveCacheProvider(options?.providerName, logContext);
90
+ if (provider === undefined) {
91
+ return result;
92
+ }
88
93
  if (options?.allEntries) {
89
94
  try {
90
95
  await provider.deleteByPattern(cacheName + '*');
91
96
  }
92
97
  catch (error) {
93
98
  (0, cache_logger_1.logCacheEvent)('cache.operation_failed', { ...logContext, operation: 'evict', error });
94
- throw error;
99
+ return result;
95
100
  }
96
101
  (0, cache_logger_1.logCacheEvent)('cache.evict_completed', { ...logContext, scope: 'allEntries' });
97
102
  }
@@ -1 +1 @@
1
- {"version":3,"file":"cache-evict.js","sourceRoot":"","sources":["../../src/decorators/cache-evict.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cache-evict.js","sourceRoot":"","sources":["../../src/decorators/cache-evict.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/decorators/cache.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAC1B,OAAO,EAKH,KAAK,eAAe,EACpB,KAAK,gBAAgB,EAExB,MAAM,qBAAqB,CAAC;AAO7B;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GAAG,IAAI,GAAG,MAAM,GAAG,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,MAAM,CAAC,CAAC;AAEhF,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,CAAC;AAElD;;GAEG;AACH,MAAM,WAAW,YAAY;IACzB;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;;;OAKG;IACH,GAAG,CAAC,EAAE,gBAAgB,CAAC;IAEvB;;OAEG;IACH,UAAU,CAAC,EAAE,gBAAgB,CAAC;CACjC;AAkMD;;;;;GAKG;AACH,wBAAgB,KAAK,CACjB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,YAAY,GACvB,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,kBAAkB,KAAK,IAAI,CA6DjF;AAED,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC"}
1
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/decorators/cache.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAC1B,OAAO,EAKH,KAAK,eAAe,EACpB,KAAK,gBAAgB,EAExB,MAAM,qBAAqB,CAAC;AAO7B;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GAAG,IAAI,GAAG,MAAM,GAAG,CAAC,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,MAAM,CAAC,CAAC;AAEhF,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,CAAC;AAElD;;GAEG;AACH,MAAM,WAAW,YAAY;IACzB;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;;;OAKG;IACH,GAAG,CAAC,EAAE,gBAAgB,CAAC;IAEvB;;OAEG;IACH,UAAU,CAAC,EAAE,gBAAgB,CAAC;CACjC;AA6LD;;;;;GAKG;AACH,wBAAgB,KAAK,CACjB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,YAAY,GACvB,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,kBAAkB,KAAK,IAAI,CAsEjF;AAED,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC"}
@@ -10,11 +10,10 @@ const key_builder_1 = require("../core/key-builder");
10
10
  const pending_cache_1 = require("../core/pending-cache");
11
11
  const pendingCache = new pending_cache_1.PendingCache();
12
12
  /**
13
- * 获取配置指向的缓存 Provider,并在解析失败时记录原始错误。
13
+ * 获取配置指向的缓存 Provider,并把解析失败转换为缓存旁路。
14
14
  * @param providerName decorator 配置中的 Provider 名称。
15
15
  * @param logContext 当前方法的稳定日志上下文。
16
- * @returns 已注册的缓存 Provider。
17
- * @throws Provider 注册表抛出的原始错误。
16
+ * @returns 已注册的缓存 Provider;解析失败时返回 undefined
18
17
  */
19
18
  function resolveCacheProvider(providerName, logContext) {
20
19
  try {
@@ -22,39 +21,27 @@ function resolveCacheProvider(providerName, logContext) {
22
21
  }
23
22
  catch (error) {
24
23
  (0, cache_logger_1.logCacheEvent)('cache.operation_failed', { ...logContext, operation: 'provider_resolution', error });
25
- throw error;
24
+ return undefined;
26
25
  }
27
26
  }
28
27
  /**
29
- * 保持 fire-and-forget 语义提交缓存写入,仅捕获调用当下可观察的同步失败。
28
+ * 保持 fire-and-forget 语义提交缓存写入,并消费同步或异步 Provider 失败。
30
29
  * @param request 写入所需 Provider、entry 和无业务数据日志上下文。
31
- * @returns 无返回值;异步写入 Promise 不会被等待或消费。
32
- * @throws Provider set 同步抛出的原始错误。
30
+ * @returns 无返回值;写入失败仅记录日志,不影响业务结果。
33
31
  */
34
32
  function dispatchCacheWrite(request) {
33
+ let operation;
35
34
  try {
36
- request.provider.set(request.cacheKey, request.entry, request.ttl);
35
+ operation = request.provider.set(request.cacheKey, request.entry, request.ttl);
37
36
  }
38
37
  catch (error) {
39
38
  (0, cache_logger_1.logCacheEvent)('cache.operation_failed', { ...request.logContext, operation: 'write', error });
40
- throw error;
39
+ return;
41
40
  }
42
41
  (0, cache_logger_1.logCacheEvent)('cache.write_dispatched', { ...request.logContext, entryType: request.entryType });
43
- }
44
- /**
45
- * 提交异常条目写入;同步 Provider 失败只记录基础设施错误,不遮蔽已经发生的业务异常。
46
- * @param request 异常写入所需 Provider、entry、TTL 和稳定日志上下文。
47
- * @returns 无返回值;异步写入 Promise 保持既有 fire-and-forget 边界。
48
- */
49
- function dispatchCacheErrorWrite(request) {
50
- try {
51
- request.provider.set(request.cacheKey, request.entry, request.ttl);
52
- }
53
- catch (error) {
42
+ void Promise.resolve(operation).catch((error) => {
54
43
  (0, cache_logger_1.logCacheEvent)('cache.operation_failed', { ...request.logContext, operation: 'write', error });
55
- return;
56
- }
57
- (0, cache_logger_1.logCacheEvent)('cache.write_dispatched', { ...request.logContext, entryType: 'error' });
44
+ });
58
45
  }
59
46
  function isErrorCacheEntry(entry) {
60
47
  return typeof entry === 'object' && entry !== null && 'error' in entry;
@@ -125,7 +112,7 @@ function handleBusinessError(request) {
125
112
  (0, cache_logger_1.logCacheEvent)('cache.error_cache_failed', { ...logContext, phase: 'encode' });
126
113
  return;
127
114
  }
128
- dispatchCacheErrorWrite({ provider, cacheKey, entry, entryType: 'error', ttl: errorPolicy.ttl, logContext });
115
+ dispatchCacheWrite({ provider, cacheKey, entry, entryType: 'error', ttl: errorPolicy.ttl, logContext });
129
116
  }
130
117
  /**
131
118
  * 解析缓存 key
@@ -173,16 +160,23 @@ function Cache(cacheName, options) {
173
160
  }
174
161
  const promise = (async () => {
175
162
  const provider = resolveCacheProvider(options?.providerName, logContext);
176
- let cached;
163
+ if (provider === undefined) {
164
+ return (await originalMethod.apply(this, args));
165
+ }
166
+ let lookupResult;
177
167
  try {
178
- cached = await provider.get(cacheKey);
168
+ const entry = await provider.get(cacheKey);
169
+ lookupResult = entry === undefined ? { type: 'miss', provider } : { type: 'hit', provider, entry };
179
170
  }
180
171
  catch (error) {
181
172
  (0, cache_logger_1.logCacheEvent)('cache.operation_failed', { ...logContext, operation: 'read', error });
182
- throw error;
173
+ lookupResult = { type: 'bypass' };
174
+ }
175
+ if (lookupResult.type === 'bypass') {
176
+ return (await originalMethod.apply(this, args));
183
177
  }
184
- if (cached !== undefined) {
185
- const cachedResult = resolveCachedEntry(cached, errorPolicy, logContext);
178
+ if (lookupResult.type === 'hit') {
179
+ const cachedResult = resolveCachedEntry(lookupResult.entry, errorPolicy, logContext);
186
180
  if (cachedResult.type === 'value') {
187
181
  return cachedResult.value;
188
182
  }
@@ -1 +1 @@
1
- {"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/decorators/cache.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/decorators/cache.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jintianxiayu/cache-decorator",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "Method caching decorators for TypeScript",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -46,37 +46,42 @@ function resolveCacheKey(
46
46
  }
47
47
 
48
48
  /**
49
- * 获取淘汰操作使用的 Provider,并在解析失败时记录原始注册表错误。
49
+ * 获取淘汰操作使用的 Provider,并把解析失败转换为缓存旁路。
50
50
  * @param providerName decorator 配置中的 Provider 名称。
51
51
  * @param logContext 当前方法的稳定日志上下文。
52
- * @returns 已注册的缓存 Provider。
53
- * @throws Provider 注册表抛出的原始错误。
52
+ * @returns 已注册的缓存 Provider;解析失败时返回 undefined
54
53
  */
55
- function resolveCacheProvider(providerName: string | undefined, logContext: CacheLogContext): CacheProvider {
54
+ function resolveCacheProvider(
55
+ providerName: string | undefined,
56
+ logContext: CacheLogContext
57
+ ): CacheProvider | undefined {
56
58
  try {
57
59
  return CacheProviderRegistry.get(providerName);
58
60
  } catch (error) {
59
61
  logCacheEvent('cache.operation_failed', { ...logContext, operation: 'provider_resolution', error });
60
- throw error;
62
+ return undefined;
61
63
  }
62
64
  }
63
65
 
64
66
  /**
65
- * 保持 fire-and-forget 语义发起单 key 删除,只处理调用当下可观察的同步失败。
67
+ * 保持 fire-and-forget 语义发起单 key 删除,并消费同步或异步 Provider 失败。
66
68
  * @param provider 当前淘汰使用的 Provider。
67
69
  * @param cacheKey 待删除的完整 key;不会进入日志元数据。
68
70
  * @param logContext 当前方法的稳定日志上下文。
69
- * @returns 无返回值;异步删除 Promise 不会被等待或消费。
70
- * @throws Provider delete 同步抛出的原始错误。
71
+ * @returns 无返回值;删除失败仅记录日志,不影响业务结果。
71
72
  */
72
73
  function dispatchCacheDelete(provider: CacheProvider, cacheKey: string, logContext: CacheLogContext): void {
74
+ let operation: void | Promise<void>;
73
75
  try {
74
- provider.delete(cacheKey);
76
+ operation = provider.delete(cacheKey);
75
77
  } catch (error) {
76
78
  logCacheEvent('cache.operation_failed', { ...logContext, operation: 'evict', error });
77
- throw error;
79
+ return;
78
80
  }
79
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
+ });
80
85
  }
81
86
 
82
87
  /**
@@ -107,13 +112,16 @@ export function CacheEvict(
107
112
  }
108
113
 
109
114
  const provider = resolveCacheProvider(options?.providerName, logContext);
115
+ if (provider === undefined) {
116
+ return result;
117
+ }
110
118
 
111
119
  if (options?.allEntries) {
112
120
  try {
113
121
  await provider.deleteByPattern(cacheName + '*');
114
122
  } catch (error) {
115
123
  logCacheEvent('cache.operation_failed', { ...logContext, operation: 'evict', error });
116
- throw error;
124
+ return result;
117
125
  }
118
126
  logCacheEvent('cache.evict_completed', { ...logContext, scope: 'allEntries' });
119
127
  } else {
@@ -80,6 +80,11 @@ interface CacheWriteRequest<T> {
80
80
  readonly logContext: CacheLogContext;
81
81
  }
82
82
 
83
+ type CacheLookupResult<T> =
84
+ | { readonly type: 'hit'; readonly provider: CacheProvider; readonly entry: CacheEntry<T> }
85
+ | { readonly type: 'miss'; readonly provider: CacheProvider }
86
+ | { readonly type: 'bypass' };
87
+
83
88
  type CacheReadResult<T> =
84
89
  | { readonly type: 'value'; readonly value: T }
85
90
  | { readonly type: 'error'; readonly error: unknown }
@@ -96,50 +101,40 @@ interface BusinessErrorRequest {
96
101
  const pendingCache = new PendingCache();
97
102
 
98
103
  /**
99
- * 获取配置指向的缓存 Provider,并在解析失败时记录原始错误。
104
+ * 获取配置指向的缓存 Provider,并把解析失败转换为缓存旁路。
100
105
  * @param providerName decorator 配置中的 Provider 名称。
101
106
  * @param logContext 当前方法的稳定日志上下文。
102
- * @returns 已注册的缓存 Provider。
103
- * @throws Provider 注册表抛出的原始错误。
107
+ * @returns 已注册的缓存 Provider;解析失败时返回 undefined
104
108
  */
105
- function resolveCacheProvider(providerName: string | undefined, logContext: CacheLogContext): CacheProvider {
109
+ function resolveCacheProvider(
110
+ providerName: string | undefined,
111
+ logContext: CacheLogContext
112
+ ): CacheProvider | undefined {
106
113
  try {
107
114
  return CacheProviderRegistry.get(providerName);
108
115
  } catch (error) {
109
116
  logCacheEvent('cache.operation_failed', { ...logContext, operation: 'provider_resolution', error });
110
- throw error;
117
+ return undefined;
111
118
  }
112
119
  }
113
120
 
114
121
  /**
115
- * 保持 fire-and-forget 语义提交缓存写入,仅捕获调用当下可观察的同步失败。
122
+ * 保持 fire-and-forget 语义提交缓存写入,并消费同步或异步 Provider 失败。
116
123
  * @param request 写入所需 Provider、entry 和无业务数据日志上下文。
117
- * @returns 无返回值;异步写入 Promise 不会被等待或消费。
118
- * @throws Provider set 同步抛出的原始错误。
124
+ * @returns 无返回值;写入失败仅记录日志,不影响业务结果。
119
125
  */
120
126
  function dispatchCacheWrite<T>(request: CacheWriteRequest<T>): void {
127
+ let operation: void | Promise<void>;
121
128
  try {
122
- request.provider.set(request.cacheKey, request.entry, request.ttl);
129
+ operation = request.provider.set(request.cacheKey, request.entry, request.ttl);
123
130
  } catch (error) {
124
131
  logCacheEvent('cache.operation_failed', { ...request.logContext, operation: 'write', error });
125
- throw error;
132
+ return;
126
133
  }
127
134
  logCacheEvent('cache.write_dispatched', { ...request.logContext, entryType: request.entryType });
128
- }
129
-
130
- /**
131
- * 提交异常条目写入;同步 Provider 失败只记录基础设施错误,不遮蔽已经发生的业务异常。
132
- * @param request 异常写入所需 Provider、entry、TTL 和稳定日志上下文。
133
- * @returns 无返回值;异步写入 Promise 保持既有 fire-and-forget 边界。
134
- */
135
- function dispatchCacheErrorWrite(request: CacheWriteRequest<never>): void {
136
- try {
137
- request.provider.set(request.cacheKey, request.entry, request.ttl);
138
- } catch (error) {
135
+ void Promise.resolve(operation).catch((error: unknown) => {
139
136
  logCacheEvent('cache.operation_failed', { ...request.logContext, operation: 'write', error });
140
- return;
141
- }
142
- logCacheEvent('cache.write_dispatched', { ...request.logContext, entryType: 'error' });
137
+ });
143
138
  }
144
139
 
145
140
  function isErrorCacheEntry<T>(entry: CacheEntry<T>): entry is ErrorCacheEntry {
@@ -215,7 +210,7 @@ function handleBusinessError(request: BusinessErrorRequest): void {
215
210
  logCacheEvent('cache.error_cache_failed', { ...logContext, phase: 'encode' });
216
211
  return;
217
212
  }
218
- dispatchCacheErrorWrite({ provider, cacheKey, entry, entryType: 'error', ttl: errorPolicy.ttl, logContext });
213
+ dispatchCacheWrite({ provider, cacheKey, entry, entryType: 'error', ttl: errorPolicy.ttl, logContext });
219
214
  }
220
215
 
221
216
  /**
@@ -274,15 +269,24 @@ export function Cache(
274
269
 
275
270
  const promise = (async () => {
276
271
  const provider = resolveCacheProvider(options?.providerName, logContext);
277
- let cached: CacheEntry<T> | undefined;
272
+ if (provider === undefined) {
273
+ return (await originalMethod.apply(this, args)) as T;
274
+ }
275
+
276
+ let lookupResult: CacheLookupResult<T>;
278
277
  try {
279
- cached = await provider.get<CacheEntry<T>>(cacheKey);
278
+ const entry = await provider.get<CacheEntry<T>>(cacheKey);
279
+ lookupResult = entry === undefined ? { type: 'miss', provider } : { type: 'hit', provider, entry };
280
280
  } catch (error) {
281
281
  logCacheEvent('cache.operation_failed', { ...logContext, operation: 'read', error });
282
- throw error;
282
+ lookupResult = { type: 'bypass' };
283
283
  }
284
- if (cached !== undefined) {
285
- const cachedResult = resolveCachedEntry(cached, errorPolicy, logContext);
284
+ if (lookupResult.type === 'bypass') {
285
+ return (await originalMethod.apply(this, args)) as T;
286
+ }
287
+
288
+ if (lookupResult.type === 'hit') {
289
+ const cachedResult = resolveCachedEntry(lookupResult.entry, errorPolicy, logContext);
286
290
  if (cachedResult.type === 'value') {
287
291
  return cachedResult.value;
288
292
  }
@@ -244,7 +244,7 @@ it('cache-operation-logging/E04 重复淘汰每次只执行一次实际删除和
244
244
  expect(events(mockLogger.debug)).toEqual(['cache.evict_dispatched', 'cache.evict_dispatched']);
245
245
  });
246
246
 
247
- it('cache-operation-logging/F03 allEntries 删除失败记录同一错误且不报告 completed', async () => {
247
+ it('cache-operation-logging/F03 allEntries 删除失败记录同一错误且保留业务结果', async () => {
248
248
  const evictionError = new Error('redis scan failed');
249
249
  const provider = createProvider();
250
250
  provider.deleteByPattern.mockRejectedValue(evictionError);
@@ -257,7 +257,7 @@ it('cache-operation-logging/F03 allEntries 删除失败记录同一错误且不
257
257
  }
258
258
  }
259
259
 
260
- await expect(new UserService().clearUsers()).rejects.toBe(evictionError);
260
+ await expect(new UserService().clearUsers()).resolves.toBe(true);
261
261
  expect(provider.deleteByPattern).toHaveBeenCalledTimes(1);
262
262
  expect(events(mockLogger.debug)).not.toContain('cache.evict_completed');
263
263
  expect(loggedMetadata(mockLogger.error)).toEqual([
@@ -272,13 +272,39 @@ it('cache-operation-logging/F03 allEntries 删除失败记录同一错误且不
272
272
  ]);
273
273
  });
274
274
 
275
- it('cache-operation-logging/F05 异步单 key 删除拒绝不会被等待或消费', async () => {
275
+ it('cache-operation-logging/F03 allEntries 同步删除失败保留业务结果且不报告 completed', async () => {
276
+ const evictionError = new Error('synchronous scan failed');
277
+ const provider = createProvider();
278
+ provider.deleteByPattern.mockImplementation(() => {
279
+ throw evictionError;
280
+ });
281
+ CacheProviderRegistry.register('sync-failed-all-provider', provider.provider);
282
+
283
+ class UserService {
284
+ @CacheEvict('sync-failed-all-users', { providerName: 'sync-failed-all-provider', allEntries: true })
285
+ clearUsers(): string {
286
+ return 'business-result';
287
+ }
288
+ }
289
+
290
+ await expect(new UserService().clearUsers()).resolves.toBe('business-result');
291
+ expect(events(mockLogger.debug)).not.toContain('cache.evict_completed');
292
+ expect(loggedMetadata(mockLogger.error)).toContainEqual({
293
+ event: 'cache.operation_failed',
294
+ cacheName: 'sync-failed-all-users',
295
+ methodName: 'clearUsers',
296
+ providerName: 'sync-failed-all-provider',
297
+ operation: 'evict',
298
+ error: evictionError,
299
+ });
300
+ });
301
+
302
+ it('cache-operation-logging/F05 异步单 key 删除拒绝被观察但不改变业务结果', async () => {
276
303
  let rejectEviction: ((reason?: unknown) => void) | undefined;
277
304
  const evictionError = new Error('async delete failed');
278
305
  const evictionPromise = new Promise<void>((_resolve, reject) => {
279
306
  rejectEviction = reject;
280
307
  });
281
- const observedEvictionFailure = evictionPromise.catch((error: unknown) => error);
282
308
  const thenSpy = jest.spyOn(evictionPromise, 'then');
283
309
  const provider = createProvider();
284
310
  provider.delete.mockReturnValue(evictionPromise);
@@ -293,7 +319,7 @@ it('cache-operation-logging/F05 异步单 key 删除拒绝不会被等待或消
293
319
 
294
320
  await expect(new UserService().deleteUser()).resolves.toBe(15);
295
321
  expect(provider.delete).toHaveBeenCalledTimes(1);
296
- expect(thenSpy).not.toHaveBeenCalled();
322
+ expect(thenSpy).toHaveBeenCalledTimes(1);
297
323
  expect(events(mockLogger.debug)).toEqual(['cache.evict_dispatched']);
298
324
  expect(events(mockLogger.debug)).not.toContain('cache.evict_completed');
299
325
 
@@ -301,25 +327,35 @@ it('cache-operation-logging/F05 异步单 key 删除拒绝不会被等待或消
301
327
  throw new Error('Eviction rejecter was not initialized');
302
328
  }
303
329
  rejectEviction(evictionError);
304
- await expect(observedEvictionFailure).resolves.toBe(evictionError);
330
+ await Promise.resolve();
331
+ expect(loggedMetadata(mockLogger.error)).toContainEqual({
332
+ event: 'cache.operation_failed',
333
+ cacheName: 'async-delete-users',
334
+ methodName: 'deleteUser',
335
+ providerName: 'async-delete-provider',
336
+ operation: 'evict',
337
+ error: evictionError,
338
+ });
305
339
  });
306
340
 
307
- it('淘汰 Provider 解析失败记录 provider_resolution 且不删除', async () => {
341
+ it('淘汰 Provider 解析失败记录 provider_resolution 且保留业务结果', async () => {
308
342
  const registryError = new Error('evict provider missing');
309
343
  jest.spyOn(CacheProviderRegistry, 'get').mockImplementation(() => {
310
344
  throw registryError;
311
345
  });
312
346
  const businessMethod = jest.fn(() => 'updated');
347
+ const resolver = jest.fn(() => 'unused-key');
313
348
 
314
349
  class UserService {
315
- @CacheEvict('missing-evict-users', { providerName: 'missing-evict-provider' })
350
+ @CacheEvict('missing-evict-users', { providerName: 'missing-evict-provider', key: resolver })
316
351
  updateUser(): string {
317
352
  return businessMethod();
318
353
  }
319
354
  }
320
355
 
321
- await expect(new UserService().updateUser()).rejects.toBe(registryError);
356
+ await expect(new UserService().updateUser()).resolves.toBe('updated');
322
357
  expect(businessMethod).toHaveBeenCalledTimes(1);
358
+ expect(resolver).not.toHaveBeenCalled();
323
359
  expect(loggedMetadata(mockLogger.error)).toEqual([
324
360
  {
325
361
  event: 'cache.operation_failed',
@@ -333,7 +369,7 @@ it('淘汰 Provider 解析失败记录 provider_resolution 且不删除', async
333
369
  expect(mockLogger.debug).not.toHaveBeenCalled();
334
370
  });
335
371
 
336
- it('同步单 key 删除失败记录 evict operation 且传播同一错误', async () => {
372
+ it('同步单 key 删除失败记录 evict operation 且保留业务结果', async () => {
337
373
  const evictionError = new Error('synchronous delete failed');
338
374
  const provider = createProvider();
339
375
  provider.delete.mockImplementation(() => {
@@ -348,7 +384,7 @@ it('同步单 key 删除失败记录 evict operation 且传播同一错误', asy
348
384
  }
349
385
  }
350
386
 
351
- await expect(new UserService().deleteUser(16)).rejects.toBe(evictionError);
387
+ await expect(new UserService().deleteUser(16)).resolves.toBe(16);
352
388
  expect(provider.delete).toHaveBeenCalledTimes(1);
353
389
  expect(events(mockLogger.debug)).not.toContain('cache.evict_dispatched');
354
390
  expect(loggedMetadata(mockLogger.error)).toContainEqual({