Files
sentinel-home-ai/.workbuddy/memory/2026-08-29.md
ericwyuan 988c63a8f9 fix(fam-core): 修复 sync_identity_map 增量同步 1062 主键冲突导致游标卡死
Oracle→NAS 增量同步反复报 (1062, "Duplicate entry '1073-汤圆' for key
'uq_video_raw_uid'"),整批失败、游标永不推进,同步状态页卡在"同步中"。

根因:sync_identity_map 以 Oracle person_identity_map.id(代理自增键)
作 NAS 主键,但 Oracle 库重建/恢复后这个 id 会被复用——实测 Oracle 现在
的 id=744 对应 (1073,'汤圆'),NAS 上 id=744 还是旧的 (1302,'人物E')。
upsert 先按 PK id 命中旧行,UPDATE 成 (1073,'汤圆') 后与已存在的同名行
在 uq_video_raw_uid 上二次冲突。

修复:镜像表改为 NAS 本地自增 id 主键,业务键 (video_id, raw_uid) 唯一,
Oracle 的 id 只落 oracle_id 列做溯源;upsert 的 UPDATE 子句不再改写
video_id/raw_uid 这两个键列,只更新 oracle_id/canonical_name/source 等。
ddl.sql 同步更新表定义。

已在 NAS 上完成现网迁移(ADD COLUMN oracle_id -> 回填 -> 主键改自增)并
验证:补拉同步成功恢复,游标正常推进,手动触发同步验证通过。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-29 23:11:10 +08:00

2.7 KiB
Raw Permalink Blame History

2026-08-29 工作日志

修复 Oracle→NAS 增量同步 1062 (uq_video_raw_uid 重复键) — 已部署验证

现象

  • 同步状态页:状态"同步中"、最近 —本次增量 —、游标卡在 2026-08-29 04:57:15
  • 报错:(1062, "Duplicate entry '1073-汤圆' for key 'uq_video_raw_uid'"),游标永不推进

根因(两层)

  1. 镜像表主键设计缺陷(主因)sync_identity_map 以 Oracle person_identity_map.id 作主键。 Oracle 端 id 是 AUTOINCREMENT 代理键,但 Oracle 库重建/恢复后 id 会被复用——实测 Oracle 现在的 id=744 是 (1073,'汤圆'),而 NAS 上 id=744 还是旧的 (1302,'人物E')。旧 upsert 的 ON DUPLICATE KEY UPDATE 先按 PK id 命中旧行,再把它 UPDATE 成 (1073,'汤圆'),与 已有的 (1073,'汤圆') 行在 uq_video_raw_uid 上二次冲突 → 1062 → 整批失败 → 游标不推进 → 每 30 分钟重复失败。
  2. 陈旧 pycache(部署陷阱)NAS 上 .pyc 时间戳比 .py 新(部署时 .py 带旧 mtime 拷贝), Python 信任 pyc 加载了旧逻辑;另曾有两个 gunicorn master 并存8487/10914只有一个绑 8000。

修复

  • db_layer.py upsert_sync_identity_map v2:镜像表改为 NAS 本地自增 id 主键, 业务键 (video_id, raw_uid) 唯一Oracle id 只落 oracle_id 列溯源UPDATE 子句不再改写 video_id/raw_uid只更新 oracle_id/canonical_name/source/updated_at/synced_at
  • scripts/ddl.sql:同步更新表定义(id INT AUTO_INCREMENT PRIMARY KEY + oracle_id INT)。
  • 现网迁移ADD COLUMN oracle_id → 回填 oracle_id=idMODIFY id AUTO_INCREMENT(无重复对, 迁移安全);修复早前测试误改的 3061 行 canonical_name。
  • 部署db_layer.py 用 stdin 管道覆盖 NAS清空全部 pycache;杀掉双 gunicorn单实例重启。

验证(通过)

  • 09:55 一次补拉成功:videos+260 events+1456 people+37 model_calls+744 identity_map+285,游标推进到 09:55:06
  • /api/statuslast_error=nulllast_sync_at 有值;手动 /api/sync/trigger ok拉 2videos/2events/1people/6calls/1identity
  • (1073,'汤圆') 行已自愈:oracle_id=744NAS 旧 (744→1302,'人物E') 保留不冲突

待办/注意

  • 本地 repo 有未提交改动:fam-core/src/fam_core/db_layer.pyscripts/ddl.sql(等用户拍板提交)
  • 同类隐患sync_videos/sync_events/sync_model_calls 同样以 Oracle id 为主键,但 Oracle 侧这些表 只插入不 UPDATE、id 稳定,风险低;若将来 Oracle 也重建,需同样改法
  • NAS 部署后必须清 pycache(或 touch .py否则旧 pyc 被加载(已踩坑)