- Create SECURITY.md with vulnerability disclosure policy - Create docs/README.md as documentation index page - Create docs/observability.md with metrics/logs/traces/alerting/SLO specs - Create docs/runbook.md with troubleshooting scenarios (RB-01 to RB-05) - Create docs/security.md with security design and compliance - Create docs/release.md with versioning and rollback strategies - Create docs/database.md with database design and migration strategy - Create docs/adr/0005-cache-error-handler.md for cache error handling ADR - Update README.md with documentation index section - Update docs/architecture.md to update B-7 rule and add trust boundary section - Create docs/api-guide.md with API specifications - Fix method names in OrderDao.java to match OrderMapper interface - Add Spring Boot Actuator dependency to tmpl-infrastructure - Fix Redis ping method in RedisHealthContributor.java - Fix syntax errors and method names in OrderAppService.java Co-authored-by: Qwen-Coder <[email protected]>
2.0 KiB
2.0 KiB
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 不可用时的过渡。