# ADR-0005: 缓存异常容错策略 **状态**: 已采纳 **日期**: 2026-02-12 **决策者**: 模板维护团队 ## 上下文 tmpl-api 通过 `@Cacheable` / `@CacheEvict` 使用 Redis 缓存。当 Redis 不可用时, Spring 默认的 `SimpleCacheErrorHandler` 会将异常直接抛出给调用方,导致本应正常的 数据库查询也返回 500 错误。这对服务可用性造成不可接受的影响。 ## 决策驱动因素 - 缓存是可选的性能优化,不应成为服务可用性的瓶颈。 - 运维团队需要在日志中看到缓存异常的完整堆栈以便排查。 - `GracefulCacheErrorHandler` 应与 `app.cache.enabled` 属性开关联动。 - ArchUnit A-3 规则要求 Application 层不直接依赖 Infrastructure 实现。 ## 考虑的方案 **方案 A — 自定义 CacheErrorHandler(已采纳)** 实现 `CachingConfigurer` 接口,注册 `GracefulCacheErrorHandler`,在 `handleCacheGetError` / `handleCachePutError` / `handleCacheEvictError` / `handleCacheClearError` 中记录 WARN 日志(含完整堆栈)并吞掉异常。 **方案 B — Resilience4j CircuitBreaker 包裹缓存调用** 为 Redis 调用添加断路器,在失败率达到阈值时自动打开。 **方案 C — 不处理,缓存失败即服务失败** 保持默认 `SimpleCacheErrorHandler`。 ## 决策 采纳方案 A。方案 B 增加了额外依赖和复杂度,可在后续迭代中作为增强引入(可与方案 A 共存)。 方案 C 不可接受,因为它让可选组件的故障影响核心功能。 ## 后果 **正面**:Redis 不可用时服务自动降级为直接查询数据库,对终端用户透明;运维可通过 WARN 日志和 `redisPing` HealthIndicator 感知异常并修复。 **负面**:降级期间数据库负载会上升;若数据库也接近瓶颈则服务仍可能受影响。 ## 后续行动 - 在 Grafana Dashboard 中为 `cache_gets_total{result="miss"}` 创建告警。 - 评估 Resilience4j CircuitBreaker 作为方案 B 的补充。 - 考虑本地 Caffeine 二级缓存作为 Redis 不可用时的过渡。