133 lines
7.8 KiB
Markdown
133 lines
7.8 KiB
Markdown
---
|
||
name: dao-tracing-refactor
|
||
overview: 按 UserCreate 的链路追踪模式,为 mysqldao(13 文件) 与 redisdao(10 文件) 中所有 DAO 方法/函数统一补全 tracing span(Start/defer End/RecordError),并保持增删改查list 顺序。
|
||
todos:
|
||
- id: mysqldao-batch-a
|
||
content: 按样板改造 mysqldao 的 user.go 剩余方法、auditlog.go、closet.go、clothes.go、collect.go、goods.go,补充 Span 追踪与错误记录
|
||
status: completed
|
||
- id: mysqldao-batch-b
|
||
content: 改造 mysqldao 的 horoscope.go、like.go、notification.go、order.go、outfit.go、quota.go、vipflowlog.go,处理 RowsAffected 分支并加追踪
|
||
status: in_progress
|
||
dependencies:
|
||
- mysqldao-batch-a
|
||
- id: redisdao-batch-a
|
||
content: 改造 redisdao 的 auth.go、closet.go、collect.go、horoscope.go、like.go,处理管道 Exec 与 redis.Nil 豁免并加追踪
|
||
status: pending
|
||
- id: redisdao-batch-b
|
||
content: 改造 redisdao 的 lock.go、notification.go、outfit.go、quota.go、user.go,处理 Lua 脚本与 redis.Nil 豁免(user.go 补充 go-redis 导入)
|
||
status: pending
|
||
dependencies:
|
||
- redisdao-batch-a
|
||
- id: verify-build
|
||
content: 执行 go build ./... 与 go vet ./... 验证编译通过、无新增告警且语义不变
|
||
status: pending
|
||
dependencies:
|
||
- mysqldao-batch-b
|
||
- redisdao-batch-b
|
||
---
|
||
|
||
## 产品概述
|
||
|
||
为 `zt-closet-backend` 的数据访问层(DAO)统一接入 OpenTelemetry 链路追踪能力,使 MySQL 与 Redis 的每个数据操作方法都具备可观测的 Span 追踪和错误记录,便于分布式追踪与线上问题定位。
|
||
|
||
## 核心功能
|
||
|
||
- 以 `UserCreate` 方法为改造样板,为 `internal/mysqldao` 下全部 DAO 方法(13 个文件)补充统一的 Span 追踪与错误记录逻辑。
|
||
- 为 `internal/redisdao` 下全部包级函数/方法(10 个文件)补充相同风格的 Span 追踪与错误记录逻辑。
|
||
- 统一改造模式:方法开头启动 Span(命名 `dao.<方法名>`)→ `defer span.End()` → 执行原有 DB/缓存调用 → 出错时先 `span.RecordError(err)` 再返回。
|
||
- 保持各方法原有业务逻辑、入参与返回值语义完全不变,仅新增追踪与错误记录。
|
||
- 对 Redis 查询"未命中"(`redis.Nil`)等属于正常业务分支的场景不记录为错误,避免污染追踪链路。
|
||
- 维持既有 "增-删-改-查-list" 的方法排列顺序。
|
||
|
||
## 技术栈
|
||
|
||
- 语言:Go
|
||
- ORM:GORM(`gorm.io/gorm`),已通过 `d.db.WithContext(ctx)` 传递上下文
|
||
- 缓存:go-redis v9(`github.com/redis/go-redis/v9`)
|
||
- 追踪:OpenTelemetry,全局追踪器 `global.Tracer`(定义于 `global/otel.go` 的 `var Tracer trace.Tracer`)
|
||
|
||
## 实现方式
|
||
|
||
基于用户提供的 `UserCreate` 样板,对每个 DAO 方法套用统一的追踪装饰模式。这是纯增量式改造,不改变任何业务语义与函数签名,风险可控、易于批量执行。
|
||
|
||
### 改造模板(mysqldao,结构体方法)
|
||
|
||
```
|
||
func (d *XxxDao) XxxCreate(ctx context.Context, ...) (...) {
|
||
ctx, span := global.Tracer.Start(ctx, "dao.XxxCreate")
|
||
defer span.End()
|
||
|
||
if err := d.db.WithContext(ctx).Create(x).Error; err != nil {
|
||
span.RecordError(err)
|
||
return err
|
||
}
|
||
return nil
|
||
}
|
||
```
|
||
|
||
- 单行 `return ...Error` 需改写为 `if err := ...; err != nil { span.RecordError(err); return err }` 形式。
|
||
- 带 `RowsAffected` 返回值的方法(如 `QuotaIncreaseUsed`、`OrderUpdateStatusByOrderNo`):先判断 `res.Error`,记录后再返回,保留 `RowsAffected > 0` 判断逻辑。
|
||
- 查询类方法(`First`/`Find`)出错统一 `span.RecordError(err)` 后返回。
|
||
- `ctx` 必须重新赋值(`ctx, span := ...`),确保新 Span 的上下文继续向下游 `WithContext(ctx)` 传递。
|
||
|
||
### 改造模板(redisdao,多为包级函数)
|
||
|
||
```
|
||
func XxxSet(ctx context.Context, ...) error {
|
||
ctx, span := global.Tracer.Start(ctx, "dao.XxxSet")
|
||
defer span.End()
|
||
|
||
if err := global.RedisEngine.Set(ctx, key, val, ttl).Err(); err != nil {
|
||
span.RecordError(err)
|
||
return err
|
||
}
|
||
return nil
|
||
}
|
||
```
|
||
|
||
- redisdao 方法原多为 `return xxx.Err()` / `return xxx.Result()`,需先捕获 error/result 再记录并返回。
|
||
- 事务管道(`TxPipeline` + `Exec`,见 `auth.go`):对 `pipe.Exec(ctx)` 返回的 err 做记录后返回。
|
||
- Lua 脚本调用(`lock.go`、`quota.go`):对 `script.Run(...)` 返回结果的 error 做记录后返回。
|
||
- **`redis.Nil` 处理策略**:对 Get 类"未命中属正常"的方法(如 `UserInfoGet`、`AuthUserIDByAccess`、`QuotaRemainGet`),仅当 `err != nil && err != redis.Nil` 时才调用 `span.RecordError(err)`,其余错误正常记录;`redis.Nil` 仍原样返回给调用方,语义不变。`quotaDailyValue` 已内部吞掉 `redis.Nil`,无需特殊处理。
|
||
|
||
### 实现要点
|
||
|
||
- **保持顺序**:延续既有 "增删改查list" 排列,不调整方法位置。
|
||
- **import**:mysqldao 各文件已含 `global`;redisdao 各文件已含 `global`,对需判断 `redis.Nil` 的文件确认已导入 `github.com/redis/go-redis/v9`(`user.go` 当前未导入,需新增)。
|
||
- **零行为变更**:不改动 SQL/Redis 命令、条件、返回值与错误传播路径,仅叠加追踪。
|
||
- **性能**:Span 创建为轻量操作,DAO 均为单次 I/O,无额外热点开销;不引入 N+1 或重复遍历。
|
||
- **安全**:仅记录 error 对象,不写入用户敏感数据(避免 PII 进入追踪属性)。
|
||
|
||
## 目录结构
|
||
|
||
```
|
||
zt-closet-backend/
|
||
├── internal/
|
||
│ ├── mysqldao/
|
||
│ │ ├── user.go # [MODIFY] UserCreate 已完成;改造 UserUpdateByID/UserGetByID/UserGetByUUID/UserGetByOpenID
|
||
│ │ ├── auditlog.go # [MODIFY] ClosetAuditLog* 与 OutfitAuditLog* 全部方法加追踪
|
||
│ │ ├── closet.go # [MODIFY] ClosetShare* 与 ClosetShareItem* 全部方法加追踪
|
||
│ │ ├── clothes.go # [MODIFY] Clothes* 全部方法加追踪
|
||
│ │ ├── collect.go # [MODIFY] 收藏相关 DAO 方法加追踪
|
||
│ │ ├── goods.go # [MODIFY] 商品相关 DAO 方法加追踪
|
||
│ │ ├── horoscope.go # [MODIFY] 星座相关 DAO 方法加追踪
|
||
│ │ ├── like.go # [MODIFY] 点赞相关 DAO 方法加追踪
|
||
│ │ ├── notification.go # [MODIFY] 通知相关 DAO 方法加追踪
|
||
│ │ ├── order.go # [MODIFY] Order* 与 RefundFlowLog* 方法加追踪(含 RowsAffected 分支)
|
||
│ │ ├── outfit.go # [MODIFY] 穿搭相关 DAO 方法加追踪
|
||
│ │ ├── quota.go # [MODIFY] Quota*/UserDailyUsage*/QuotaFlowLog* 方法加追踪(含 RowsAffected 分支)
|
||
│ │ └── vipflowlog.go # [MODIFY] VIP 流水相关 DAO 方法加追踪
|
||
│ └── redisdao/
|
||
│ ├── auth.go # [MODIFY] AuthTokenSet/Del(管道 Exec)、AuthUserIDBy*(redis.Nil 豁免)
|
||
│ ├── closet.go # [MODIFY] 衣柜缓存相关函数加追踪
|
||
│ ├── collect.go # [MODIFY] 收藏缓存相关函数加追踪
|
||
│ ├── horoscope.go # [MODIFY] 星座缓存相关函数加追踪
|
||
│ ├── like.go # [MODIFY] LikeAdd/Remove/IsMember/Count 加追踪
|
||
│ ├── lock.go # [MODIFY] TryLock/Unlock(Lua 脚本)加追踪
|
||
│ ├── notification.go # [MODIFY] 通知缓存相关函数加追踪
|
||
│ ├── outfit.go # [MODIFY] 穿搭缓存相关函数加追踪
|
||
│ ├── quota.go # [MODIFY] Quota 缓存/Lua 脚本相关函数加追踪
|
||
│ └── user.go # [MODIFY] UserInfoSet/Del/Get(Get 需 redis.Nil 豁免,新增 go-redis 导入)
|
||
├── global/
|
||
│ └── otel.go # [无需修改] 提供 global.Tracer
|
||
``` |