Files
zt-closet-backend/.codebuddy/plans/model-call-retry_5868b00a.md
T
2026-09-11 01:41:52 +08:00

5.3 KiB
Raw Blame History

name, overview, todos
name overview todos
model-call-retry 为抠图(Matting)与分类(Classification)两个模型调用增加可配置的自动重试机制,默认最大重试 3 次;配置项分别加到 MattingModel 与 ClassificationModel 配置段下,并在衣物异步处理流程中接入重试逻辑。
id content status
add-retry-config 在 section.go 结构体及 config.yaml、config.example.yaml 中为 MattingModel/ClassificationModel 增加 MaxRetry 配置 completed
id content status dependencies
default-retry-value 在 main.go 读取配置后对两个模型 MaxRetry completed
add-retry-config

产品概述

为衣物图片处理流水线中的抠图(Matting)与分类(Classification)两个模型调用增加自动重试能力,降低下游模型服务偶发失败导致的衣物处理失败率。重试次数通过配置文件分别控制,默认最大重试 3 次。

核心功能

  • 抠图调用失败时自动重试,默认最大重试 3 次(总尝试 4 次)
  • 分类调用失败时自动重试,默认最大重试 3 次(总尝试 4 次)
  • 重试次数分别配置在 MattingModel 与 ClassificationModel 两个配置段下,配置缺失或小于等于 0 时兜底为 3
  • 全部重试失败后维持现有失败处理:抠图失败回退上传额度并标记失败;分类失败保留已抠图结果(processed_image)并标记失败,便于用户手动补全
  • 删除处理中衣物不回退额度的既有防滥用设计保持不变(不做修改)

技术栈

  • Go 1.27(使用泛型、log/slog、context + time.After)
  • 配置:viper(复用现有 setting.ReadSection 读取模式)
  • 日志:log/slog(复用现有 slog.WarnContext / slog.ErrorContext)

实现方案

总体策略

复用现有配置读取与兜底模式,在配置结构体、配置文件中增加 MaxRetry 字段;在异步处理函数 processImageAsync 中新增一个泛型重试辅助函数 retryCall,包裹抠图与分类两处调用,实现失败自动重试,失败分支逻辑保持不变。

关键设计

  1. 配置层(最小改动)
  • pkg/setting/section.go 的 MattingModelSettingS、ClassificationModelSettingS 各新增 MaxRetry int 字段。
  • configs/config.yaml(实际配置)与 configs/config.example.yaml(示例配置)的 MattingModel、ClassificationModel 段各新增 MaxRetry: 3。
  • main.go 在读取 MattingModel、ClassificationModel 后,对 MaxRetry <= 0 兜底为 3,与现有 MaxSizeMB <= 0 兜底 20、Model == "" 兜底 hy4 的模式保持一致。
  1. 重试层(泛型辅助函数)
  • 在 internal/service/clothes.go 新增 retryCall[T any] 泛型函数:循环尝试 maxRetry+1 次;每次失败记录 slog.WarnContext;重试间隔使用 select + time.After(1s) + ctx.Done(),保证 ctx 到期时提前终止。
  • 抠图与分类调用分别用 retryCall 包裹,maxRetry 分别取 global.MattingModelSetting.MaxRetry 与 global.ClassificationModelSetting.MaxRetry。
  • fn 闭包捕获外部 ctx,每次调用内部会重建 multipart body,无需额外重置状态。
  1. 失败分支保持不变
  • 抠图全部重试失败后仍调用 markProcessFailed(标记失败 + 回退上传额度)。
  • 分类全部重试失败后仍保留 processed_image 并标记 ImageProcessFailed(不回退额度,允许用户手动补全)。

关键代码结构

// retryCall 对 fn 进行最多 maxRetry 次额外重试(总尝试 maxRetry+1 次)。
// 重试间隔 1s;ctx 到期则提前终止并返回 ctx.Err()。
func retryCall[T any](ctx context.Context, maxRetry int, name string, fn func() (T, error)) (T, error)

// 调用示例
processedData, err := retryCall(ctx, global.MattingModelSetting.MaxRetry, "matting", func() ([]byte, error) {
    return mattingClient.Matting(ctx, imageData)
})

实现注意

  • MaxRetry 语义:失败后的额外重试次数,总尝试次数 = MaxRetry + 1;默认 3 即总尝试 4 次。
  • 超时预算:异步 ctx 总超时为 3 分钟,单次 HTTP 客户端超时为 120s。retryCall 通过 ctx.Done() 检查避免超时后继续重试;瞬时失败(连接拒绝、偶发非 200)通常快速返回,3 分钟预算足够。
  • 并发限流不受影响:imageProcessSem 信号量在 processImageAsync 入口已获取,重试发生在同一 goroutine 内,不会放大并发。
  • blast radius 控制:仅新增配置字段与重试辅助函数,不修改 DeleteByUUID 额度回退逻辑;config.yaml 中重复的 UserDefaultQuota 段为既有问题,本次不处理。

目录结构

zt-closet-backend/
├── pkg/setting/section.go          # [MODIFY] MattingModelSettingS、ClassificationModelSettingS 各新增 MaxRetry int 字段
├── configs/config.yaml             # [MODIFY] MattingModel、ClassificationModel 段各新增 MaxRetry: 3
├── configs/config.example.yaml     # [MODIFY] MattingModel、ClassificationModel 段各新增 MaxRetry: 3
├── main.go                         # [MODIFY] 读取 MattingModel、ClassificationModel 后对 MaxRetry<=0 兜底为 3
└── internal/service/clothes.go     # [MODIFY] 新增 retryCall 泛型辅助函数,抠图/分类调用接入重试