mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-04 15:06:37 +08:00
3.8 KiB
3.8 KiB
name, description
| name | description |
|---|---|
| cache-framework | Wavelet 项目专用:当新增或修改基于 Cordis 插件的业务缓存、ctx.Cache() / contracts.CacheService 访问、三层读路径(RAM L1 + Redis L2 + DB L3)、多节点 Pub/Sub 失效同步时必须使用。 |
系统三层缓存框架与开发规范 (Cordis 插件化架构)
本技能指导 Wavelet 在 Cordis 架构下,如何使用平台统一提供的三层缓存服务(ctx.Cache() 与 contracts.CacheService)进行高性能缓存读写与分布式失效同步。
1. 三层读路径与标准契约
Wavelet 标准读路径为 本地 RAM (L1) → Redis (L2) → Database (L3)(由快到慢):
| 层级 | 技术 | 职责 |
|---|---|---|
| L1 本地 | pkg/cache/ram (Otter) |
进程内纳秒级极速读取,抗最高频热点流量 |
| L2 共享 | Redis 序列化缓存 | 跨节点共享,具备 TTL 与防击穿保护 |
| L3 权威 | 关系型数据库 (PostgreSQL / SQLite) | 唯一权威数据源 |
标准接口契约 (contracts.CacheService)
type CacheService interface {
// Get 从缓存获取并反序列化至 target,若不存在返回 ErrCacheMiss
Get(ctx context.Context, key string, target any) error
// Set 存储对象至缓存并设置 TTL
Set(ctx context.Context, key string, value any, ttl time.Duration) error
// Delete 彻底移除缓存(清空本地 RAM、删除 Redis 并广播 Pub/Sub 通知全集群清空 RAM)
Delete(ctx context.Context, key string) error
// GetOrSet 优先读缓存,若未命中则执行 loader 回源加载并自动回写
GetOrSet(ctx context.Context, key string, target any, ttl time.Duration, loader func() (any, error)) error
// Invalidate 是 Delete 的语义别名
Invalidate(ctx context.Context, key string) error
}
2. 业务使用标准范式
2.1 高性能读穿透 (GetOrSet)
业务 Service 推荐优先使用 GetOrSet,框架底层自动完成 L1/L2 穿透、回写及并发防击穿:
func (s *OrderService) GetOrderWithCache(ctx context.Context, orderID string) (*Order, error) {
var order Order
cacheKey := "order:" + orderID
err := s.cache.GetOrSet(ctx, cacheKey, &order, 10*time.Minute, func() (any, error) {
// Cache Miss: 执行 DB 回源查询
var dbOrder Order
if err := s.db.WithContext(ctx).First(&dbOrder, "id = ?", orderID).Error; err != nil {
return nil, err
}
return &dbOrder, nil
})
if err != nil {
return nil, err
}
return &order, nil
}
2.2 数据变更与失效广播 (Invalidate / Delete)
凡涉及数据创建、修改、软删除、状态变更的入口(包含 HTTP Handler、后台 Worker 任务、定时清理任务),必须调用缓存失效:
func (s *OrderService) UpdateOrderStatus(ctx context.Context, orderID string, newStatus string) error {
// 1. 更新数据库权威数据
if err := s.db.WithContext(ctx).Model(&Order{}).Where("id = ?", orderID).Update("status", newStatus).Error; err != nil {
return err
}
// 2. 广播失效缓存(自动清除本机 L1、删除 Redis L2,并向集群广播 Pub/Sub 消息清空其他节点 L1)
return s.cache.Invalidate(ctx, "order:"+orderID)
}
3. 核心规则与禁止写法 (Guardrails)
- 严禁自研本地 map 缓存:
- 严禁在插件内编写
sync.RWMutex + map[string]Xxx的裸内存缓存,无法感知多节点数据变更,必然引发多机脏读。
- 严禁在插件内编写
- 写路径必须全覆盖失效:
- 不仅在 API 修改时失效,后台 Worker、定时任务执行数据清理或变更时,必须同步触发
cache.Invalidate。
- 不仅在 API 修改时失效,后台 Worker、定时任务执行数据清理或变更时,必须同步触发
- Key 命名空间规范:
- 缓存 Key 必须带插件命名空间前缀(如
order:meta:{id}、auth:session:{token})。
- 缓存 Key 必须带插件命名空间前缀(如
- 不可在业务高频读接口中绕过缓存直查 DB。
4. 质量与测试验证
make format
make code-check
go test ./plugins/...