mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-07 16:16:37 +08:00
108 lines
3.8 KiB
Markdown
108 lines
3.8 KiB
Markdown
---
|
||
name: "cache-framework"
|
||
description: "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`)
|
||
|
||
```go
|
||
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 穿透、回写及并发防击穿:
|
||
|
||
```go
|
||
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 任务、定时清理任务**),必须调用缓存失效:
|
||
|
||
```go
|
||
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)
|
||
|
||
1. **严禁自研本地 map 缓存**:
|
||
- 严禁在插件内编写 `sync.RWMutex + map[string]Xxx` 的裸内存缓存,无法感知多节点数据变更,必然引发多机脏读。
|
||
2. **写路径必须全覆盖失效**:
|
||
- 不仅在 API 修改时失效,后台 Worker、定时任务执行数据清理或变更时,必须同步触发 `cache.Invalidate`。
|
||
3. **Key 命名空间规范**:
|
||
- 缓存 Key 必须带插件命名空间前缀(如 `order:meta:{id}`、`auth:session:{token}`)。
|
||
4. **不可在业务高频读接口中绕过缓存直查 DB**。
|
||
|
||
---
|
||
|
||
## 4. 质量与测试验证
|
||
|
||
```bash
|
||
make format
|
||
make code-check
|
||
go test ./plugins/...
|
||
``` |