Admin:恢复 SYB 与目录并发写入 SKU 的唯一键冲突 #283

Open
opened 2026-08-21 09:11:47 +08:00 by ila · 1 comment
Owner

基本信息

  • 类型:缺陷
  • 父级大工单:#14
  • 所属 MVP / 版本:#15 / MVP 后续修复
  • 阶段:顺运宝同步可靠性
  • 关联:#46(顺运宝同步)、#59(商品目录 SKU 写入)

要解决什么

SYB 同步写入货运单 260820U552NENE 时,商品目录 xlsx-processed 与 SYB 同步并发写入相同 (goods_id, spec_key),触发 shopee_skus.uq_shopee_skus_spec 唯一键错误,导致本次同步停止。

已确认:目录 SKU 在失败同步运行期间创建;后续重跑 SYB 同步成功。问题不是上游规格重复,而是 UpsertCatalogShopeeSKU 的“先查询、后 INSERT”不是并发安全的原子 upsert。

做什么 / 不做什么

做:

  • 对 shopee_skus 的规格唯一键并发撞键进行安全恢复;
  • 撞键后重新读取已提交 SKU,并沿用既有人工优先、来源优先级与 fill_missing 规则;
  • 增加覆盖目录与 SYB 同时写入同一规格的回归测试。

不做:

  • 不删除、合并或重写已有生产 SKU;
  • 不增加 SYB 与商品目录导入的全局互斥���
  • 不改变同步范围、游标推进、规格键或来源优先级;
  • 不修改采购、PDD 和 Client 接口。

已确认的实现方案

  1. 将 UpsertCatalogShopeeSKU 的创建分支抽成“读取当前记录后应用既有规则”的内部路径。
  2. INSERT 出现 MySQL 1062 且键为 uq_shopee_skus_spec 时,重新以当前读读取该规格记录并进入已有更新逻辑;其他数据库错误仍原样返回。
  3. 重读后仍不存在或无法安全识别冲突时返回明确错误,不吞掉未知错误。
  4. 使用 MySQL 集成测试模拟首查未命中、竞争方插入成功、本次 INSERT 撞键后的重读,验证 SYB 的低优先级写入不会覆盖 xlsx-processed 或人工字段。

预计修改文件:

  • admin/repository/catalog_import.go
  • admin/repository/catalog_import_test.go 或新增对应 MySQL 集成测试文件

验收标准

  • uq_shopee_skus_spec 并发重复键不再使 SYB 同步失败。
  • 竞争方目录 SKU 仍保留原记录 ID、来源、非空字段和人工字段。
  • SYB 仅按现有 fill_missing 规则补空字段,不覆盖目录或人工字段。
  • 非目标唯一键、重读失败和其他数据库错误仍明确失败。
  • 现有商品目录、SYB 同步和数据库集成测试通过。

验证方式

从 admin/ 目录执行:

$env:GOTOOLCHAIN="go1.23.0"
go test ./repository ./service -count=1
go build ./...
Remove-Item Env:GOTOOLCHAIN

真实验证:目录导入与 SYB 同步重叠时,同一规格只保留一条 SKU,SYB 同步完成;不执行采购或付款。

风险和回退

风险:把未知唯一键冲突误判为可恢复冲突,可能隐藏数据问题。控制方式:只恢复命名为 uq_shopee_skus_spec 的 MySQL 1062;重读后必须得到同一 (goods_id, spec_key) 记录,其他情况仍返回错误。

回退:撤销本工单代码提交即可;本任务不含迁移和数据改写。

## 基本信息 - 类型:缺陷 - 父级大工单:#14 - 所属 MVP / 版本:#15 / MVP 后续修复 - 阶段:顺运宝同步可靠性 - 关联:#46(顺运宝同步)、#59(商品目录 SKU 写入) ## 要解决什么 SYB 同步写入货运单 `260820U552NENE` 时,商品目录 `xlsx-processed` 与 SYB 同步并发写入相同 `(goods_id, spec_key)`,触发 `shopee_skus.uq_shopee_skus_spec` 唯一键错误,导致本次同步停止。 已确认:目录 SKU 在失败同步运行期间创建;后续重跑 SYB 同步成功。问题不是上游规格重复,而是 `UpsertCatalogShopeeSKU` 的“先查询、后 INSERT”不是并发安全的原子 upsert。 ## 做什么 / 不做什么 做: - 对 `shopee_skus` 的规格唯一键并发撞键进行安全恢复; - 撞键后重新读取已提交 SKU,并沿用既有人工优先、来源优先级与 `fill_missing` 规则; - 增加覆盖目录与 SYB 同时写入同一规格的回归测试。 不做: - 不删除、合并或重写已有生产 SKU; - 不增加 SYB 与商品目录导入的全局互斥��� - 不改变同步范围、游标推进、规格键或来源优先级; - 不修改采购、PDD 和 Client 接口。 ## 已确认的实现方案 1. 将 `UpsertCatalogShopeeSKU` 的创建分支抽成“读取当前记录后应用既有规则”的内部路径。 2. `INSERT` 出现 MySQL 1062 且键为 `uq_shopee_skus_spec` 时,重新以当前读读取该规格记录并进入已有更新逻辑;其他数据库错误仍原样返回。 3. 重读后仍不存在或无法安全识别冲突时返回明确错误,不吞掉未知错误。 4. 使用 MySQL 集成测试模拟首查未命中、竞争方插入成功、本次 INSERT 撞键后的重读,验证 SYB 的低优先级写入不会覆盖 `xlsx-processed` 或人工字段。 预计修改文件: - `admin/repository/catalog_import.go` - `admin/repository/catalog_import_test.go` 或新增对应 MySQL 集成测试文件 ## 验收标准 - [ ] `uq_shopee_skus_spec` 并发重复键不再使 SYB 同步失败。 - [ ] 竞争方目录 SKU 仍保留原记录 ID、来源、非空字段和人工字段。 - [ ] SYB 仅按现有 `fill_missing` 规则补空字段,不覆盖目录或人工字段。 - [ ] 非目标唯一键、重读失败和其他数据库错误仍明确失败。 - [ ] 现有商品目录、SYB 同步和数据库集成测试通过。 ## 验证方式 从 `admin/` 目录执行: ```powershell $env:GOTOOLCHAIN="go1.23.0" go test ./repository ./service -count=1 go build ./... Remove-Item Env:GOTOOLCHAIN ``` 真实验证:目录导入与 SYB 同步重叠时,同一规格只保留一条 SKU,SYB 同步完成;不执行采购或付款。 ## 风险和回退 风险:把未知唯一键冲突误判为可恢复冲突,可能隐藏数据问题。控制方式:只恢复命名为 `uq_shopee_skus_spec` 的 MySQL 1062;重读后必须得到同一 `(goods_id, spec_key)` 记录,其他情况仍返回错误。 回退:撤销本工单代码提交即可;本任务不含迁移和数据改写。
Author
Owner

实现完成,状态:待验收。

实现:

  • 仅恢复 MySQL 1062 且索引为 uq_shopee_skus_spec 的并发冲突;
  • 冲突后通过 SELECT ... FOR UPDATE 当前读取得竞争方已提交 SKU;
  • 重读后继续既有 fill_missing、来源优先级和人工字段保护;
  • 其他唯一键冲突及数据库错误仍原样失败。

验证:

  • $env:GOTOOLCHAIN="go1.23.0"; go vet ./...; go test ./... -count=1
  • $env:GOTOOLCHAIN="go1.23.0"; go build ./...
  • 结果:全部通过。
  • 单元测试覆盖:首查未命中、竞争方目录 SKU 提交、1062、当前读重读、SYB 不覆盖目录字段;非规格唯一键仍失败。
  • 未执行生产写操作;未在独立 MySQL _test 库制造真实并发事务。

提交:

  • 05b04e0 fix: 恢复并发 SKU 规格唯一键冲突 (#283)
  • 9c22ec6 docs: 归档任务 #283

归档:docs/task/283-恢复并发SKU规格唯一键冲突.md

实现完成,状态:待验收。 实现: - 仅恢复 MySQL 1062 且索引为 `uq_shopee_skus_spec` 的并发冲突; - 冲突后通过 `SELECT ... FOR UPDATE` 当前读取得竞争方已提交 SKU; - 重读后继续既有 `fill_missing`、来源优先级和人工字段保护; - 其他唯一键冲突及数据库错误仍原样失败。 验证: - `$env:GOTOOLCHAIN="go1.23.0"; go vet ./...; go test ./... -count=1` - `$env:GOTOOLCHAIN="go1.23.0"; go build ./...` - 结果:全部通过。 - 单元测试覆盖:首查未命中、竞争方目录 SKU 提交、1062、当前读重读、SYB 不覆盖目录字段;非规格唯一键仍失败。 - 未执行生产写操作;未在独立 MySQL `_test` 库制造真实并发事务。 提交: - 05b04e0 `fix: 恢复并发 SKU 规格唯一键冲突 (#283)` - 9c22ec6 `docs: 归档任务 #283` 归档:`docs/task/283-恢复并发SKU规格唯一键冲突.md`
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: chengma/cmautobuy#283