[SEN] 兼容旧媒体路由 path 唯一约束迁移 #95

Closed
opened 2026-08-15 15:37:59 +08:00 by ila · 4 comments
Owner

状态

已完成

基本信息

  • 类型:缺陷 / PostgreSQL 旧库兼容迁移
  • 任务类型:单项目独立任务
  • 主项目:Sense
  • 主 agent:Sense agent
  • 所属 Epic:#7
  • 所属 MVP:#8
  • 相关工单:#67、#70、#92
  • 前置工单:#92(已验收并合入 dev)
  • 独立运行:Brain、Bell 均不启动时完成复现和验收

原始需求与追溯

  • 来源:用户于 2026-08-15 明确确认“#92 验收通过,继续”,在 #70 Windows 包吸收 #92、完成仓库外备份并执行真实迁移时发现。
  • 现象:#92 已把 sense_devices.capabilities 成功转换为 jsonb NOT NULL DEFAULT '[]';随后媒体迁移报 SQLSTATE 42704:约束 uni_sense_media_routes_path 不存在。
  • 只读诊断:旧库 sense_media_routes.path 由 PostgreSQL 唯一约束 sense_media_routes_path_key 保证唯一;新 GORM 模型使用 uniqueIndex,AutoMigrate 把列识别为旧唯一约束,却按推导名称删除 uni_sense_media_routes_path,名称不一致导致失败。媒体迁移版本 2026081419000 未登记,失败事务未留下新增列或菜单半成品。

目标

兼容旧库中由 PostgreSQL 自动命名的 path 唯一约束,在不丢失媒体路由和唯一性保护的前提下,让媒体迁移完成并可重复执行。

做什么

  • 在 2026081419000 媒体迁移的 AutoMigrate 前增加 PostgreSQL 专用旧唯一约束兼容步骤。
  • 仅处理已存在的 sense_media_routes,识别只覆盖 path 的旧唯一约束。
  • 在事务和表锁保护下先建立新模型期望的唯一索引,再按数据库实际约束名移除旧约束,随后继续 AutoMigrate。
  • 未知复合约束、重复 path 或无法确认的结构必须停止并回滚,不猜测、不删除路由。
  • 增加隔离 PostgreSQL 回归,覆盖实际旧表结构、保留记录、唯一性、无表、新库、重复执行与失败回滚。
  • 更新迁移排错 Wiki 和任务归档。

不做什么

不删除媒体路由;不清空数据库;不修改设备凭据或客户配置;不修改 Brain、Bell、共享契约或根级部署;不把本缺陷混入已验收的 #92。

写路径

  • Sense/server/cmd/migrate/migration/version/2026081419000_media.go
  • Sense/server/cmd/migrate/migration/version/2026081419000_media_test.go
  • Wiki Troubleshooting
  • docs/06-troubleshooting.md
  • docs/task/<本工单归档>.md
  • wiki-docs.json

禁止写入:Brain/**、Bell/**、contracts/**、Sense/dist/**、当前用户数据库数据(正式验证阶段仅在已确认并有可恢复备份后执行)。

建议方案

对 PostgreSQL 旧表执行可审计的兼容步骤:锁表;确认现有 path 唯一约束;创建新模型命名的唯一索引以持续保护唯一性;使用 pg_catalog 返回的实际约束名安全移除旧约束;再运行现有 AutoMigrate。整个迁移保持单事务,任一步失败全部回滚。

风险与回退

  • 风险:数据库约束/索引变更属于高风险;错误识别可能削弱 path 唯一性。
  • 控制:限定方言、表、列和单列唯一约束;表锁;先建新唯一索引再移除旧约束;隔离 PostgreSQL 测试;真实库已有 custom-format 备份且已通过 pg_restore --list。
  • 回退:失败事务自动回滚;真实迁移成功后如需回退,停止服务并从迁移前备份恢复。

验收标准

  • 旧约束名不再触发 SQLSTATE 42704
  • 既有媒体路由记录完整保留
  • 迁移前后 path 始终具有唯一性保护
  • 无表、新库、已迁移库和重复执行兼容
  • 未知或不安全结构拒绝迁移并完整回滚
  • 隔离 PostgreSQL、Go 全量 test/vet/build 与定向 race 通过
  • 重新生成 #70 Windows 包后,基于已验证备份完成真实迁移和启动 smoke
  • Wiki 镜像与任务归档一致

验证方式

  • 隔离 PostgreSQL 17 构造与当前旧库一致的表、约束和路由夹具。
  • 查询约束、索引、记录、迁移版本和回滚状态。
  • go test ./cmd/migrate/migration/version、go test ./...、go vet ./...、go build ./...。
  • 重建并审计 Windows 包;备份后运行 migration/start/health/stop smoke。

文档影响

更新 PostgreSQL 媒体路由旧唯一约束兼容、备份要求、失败处理和重试方式。

实施中变化记录

  • 2026-08-15:隔离 PostgreSQL 使用与真实旧库一致的非空路由表验证时,约束名兼容已越过 SQLSTATE 42704,但 AutoMigrate 随后因直接新增 source_ready boolean NOT NULL 且旧行为空而报 SQLSTATE 23502。为满足已确认的“保留既有路由并完成迁移”,兼容步骤扩展为在同一 ACCESS EXCLUSIVE 锁和事务内添加/回填新运行态列:source_ready=false、failure_count=0、last_error_code='',再设置 NOT NULL 并继续 AutoMigrate。以上值表示迁移后需重新对账的保守初始状态,不伪造媒体已就绪;失败仍整体回滚。

实施结果与证据

  • 实现提交:3dd4890;排错文档提交:6257859;待验收归档提交:d0185ca。
  • PR:#96(fix/95-sense-media-path-constraint → dev),已合入 dev,merge commit 12e1bff8b4af0ced2b4f040654e6440c59d60b3d。
  • Wiki:Troubleshooting revision 1a452e9aafdfe01580f37f9179584b89516cf992;任务归档 revision 89f668bc6f3308edbc1ce874c9b0a189dd418d42。
  • 隔离 PostgreSQL 17 已覆盖旧 2 路由、空库、无表、重复执行、唯一性冲突和不安全复合约束回滚;测试库运行后删除。
  • go test ./...、go vet ./...、go build ./... 与定向 PostgreSQL race 测试通过。
  • 真实本机数据库迁移成功:2 条既有路由保留;运行态必填列无空值;2026081419000 已登记;idx_sense_media_routes_path 唯一索引有效。
  • #70 Windows 包完成重建和审计;ZIP SHA-256:7E35394F6377F9F4AB0046E8413F29B140D6F301383C54C87CE871B435EF253D。Web、SPA、health、MediaMTX 均返回 200,停止脚本执行后相关端口无监听。
  • git diff --check 和任务 Wiki 定向同步检查通过。Harness strict 仅被既存 #66/#67 归档格式问题阻塞,与本任务改动无关。
  • 未验证:客户全新 Windows 主机、客户生产 PostgreSQL 账号、真实获准摄像机环境仍待人工验收。

人工验收(2026-08-16)

  • 用户明确验收通过 #95。
  • PR #96 已合入 dev,merge commit:12e1bff8b4af0ced2b4f040654e6440c59d60b3d;main 未变更。
  • Wiki 归档 revision:5f3bdf3786f2a72dac5d29236ff466743e2912b5;验收镜像提交:15bd0008163704da6797214545d0cb94fc3d2e50。
## 状态 已完成 ## 基本信息 - 类型:缺陷 / PostgreSQL 旧库兼容迁移 - 任务类型:单项目独立任务 - 主项目:Sense - 主 agent:Sense agent - 所属 Epic:#7 - 所属 MVP:#8 - 相关工单:#67、#70、#92 - 前置工单:#92(已验收并合入 `dev`) - 独立运行:Brain、Bell 均不启动时完成复现和验收 ## 原始需求与追溯 - 来源:用户于 2026-08-15 明确确认“#92 验收通过,继续”,在 #70 Windows 包吸收 #92、完成仓库外备份并执行真实迁移时发现。 - 现象:#92 已把 `sense_devices.capabilities` 成功转换为 `jsonb NOT NULL DEFAULT '[]'`;随后媒体迁移报 SQLSTATE 42704:约束 `uni_sense_media_routes_path` 不存在。 - 只读诊断:旧库 `sense_media_routes.path` 由 PostgreSQL 唯一约束 `sense_media_routes_path_key` 保证唯一;新 GORM 模型使用 `uniqueIndex`,AutoMigrate 把列识别为旧唯一约束,却按推导名称删除 `uni_sense_media_routes_path`,名称不一致导致失败。媒体迁移版本 `2026081419000` 未登记,失败事务未留下新增列或菜单半成品。 ## 目标 兼容旧库中由 PostgreSQL 自动命名的 `path` 唯一约束,在不丢失媒体路由和唯一性保护的前提下,让媒体迁移完成并可重复执行。 ## 做什么 - 在 `2026081419000` 媒体迁移的 AutoMigrate 前增加 PostgreSQL 专用旧唯一约束兼容步骤。 - 仅处理已存在的 `sense_media_routes`,识别只覆盖 `path` 的旧唯一约束。 - 在事务和表锁保护下先建立新模型期望的唯一索引,再按数据库实际约束名移除旧约束,随后继续 AutoMigrate。 - 未知复合约束、重复 path 或无法确认的结构必须停止并回滚,不猜测、不删除路由。 - 增加隔离 PostgreSQL 回归,覆盖实际旧表结构、保留记录、唯一性、无表、新库、重复执行与失败回滚。 - 更新迁移排错 Wiki 和任务归档。 ## 不做什么 不删除媒体路由;不清空数据库;不修改设备凭据或客户配置;不修改 Brain、Bell、共享契约或根级部署;不把本缺陷混入已验收的 #92。 ## 写路径 - `Sense/server/cmd/migrate/migration/version/2026081419000_media.go` - `Sense/server/cmd/migrate/migration/version/2026081419000_media_test.go` - Wiki `Troubleshooting` - `docs/06-troubleshooting.md` - `docs/task/<本工单归档>.md` - `wiki-docs.json` 禁止写入:`Brain/**`、`Bell/**`、`contracts/**`、`Sense/dist/**`、当前用户数据库数据(正式验证阶段仅在已确认并有可恢复备份后执行)。 ## 建议方案 对 PostgreSQL 旧表执行可审计的兼容步骤:锁表;确认现有 path 唯一约束;创建新模型命名的唯一索引以持续保护唯一性;使用 pg_catalog 返回的实际约束名安全移除旧约束;再运行现有 AutoMigrate。整个迁移保持单事务,任一步失败全部回滚。 ## 风险与回退 - 风险:数据库约束/索引变更属于高风险;错误识别可能削弱 path 唯一性。 - 控制:限定方言、表、列和单列唯一约束;表锁;先建新唯一索引再移除旧约束;隔离 PostgreSQL 测试;真实库已有 custom-format 备份且已通过 `pg_restore --list`。 - 回退:失败事务自动回滚;真实迁移成功后如需回退,停止服务并从迁移前备份恢复。 ## 验收标准 - [x] 旧约束名不再触发 SQLSTATE 42704 - [x] 既有媒体路由记录完整保留 - [x] 迁移前后 `path` 始终具有唯一性保护 - [x] 无表、新库、已迁移库和重复执行兼容 - [x] 未知或不安全结构拒绝迁移并完整回滚 - [x] 隔离 PostgreSQL、Go 全量 test/vet/build 与定向 race 通过 - [x] 重新生成 #70 Windows 包后,基于已验证备份完成真实迁移和启动 smoke - [x] Wiki 镜像与任务归档一致 ## 验证方式 - 隔离 PostgreSQL 17 构造与当前旧库一致的表、约束和路由夹具。 - 查询约束、索引、记录、迁移版本和回滚状态。 - `go test ./cmd/migrate/migration/version`、`go test ./...`、`go vet ./...`、`go build ./...`。 - 重建并审计 Windows 包;备份后运行 migration/start/health/stop smoke。 ## 文档影响 更新 PostgreSQL 媒体路由旧唯一约束兼容、备份要求、失败处理和重试方式。 ## 实施中变化记录 - 2026-08-15:隔离 PostgreSQL 使用与真实旧库一致的非空路由表验证时,约束名兼容已越过 SQLSTATE 42704,但 AutoMigrate 随后因直接新增 `source_ready boolean NOT NULL` 且旧行为空而报 SQLSTATE 23502。为满足已确认的“保留既有路由并完成迁移”,兼容步骤扩展为在同一 ACCESS EXCLUSIVE 锁和事务内添加/回填新运行态列:`source_ready=false`、`failure_count=0`、`last_error_code=''`,再设置 NOT NULL 并继续 AutoMigrate。以上值表示迁移后需重新对账的保守初始状态,不伪造媒体已就绪;失败仍整体回滚。 ## 实施结果与证据 - 实现提交:`3dd4890`;排错文档提交:`6257859`;待验收归档提交:`d0185ca`。 - PR:#96(`fix/95-sense-media-path-constraint → dev`),已合入 `dev`,merge commit `12e1bff8b4af0ced2b4f040654e6440c59d60b3d`。 - Wiki:Troubleshooting revision `1a452e9aafdfe01580f37f9179584b89516cf992`;任务归档 revision `89f668bc6f3308edbc1ce874c9b0a189dd418d42`。 - 隔离 PostgreSQL 17 已覆盖旧 2 路由、空库、无表、重复执行、唯一性冲突和不安全复合约束回滚;测试库运行后删除。 - `go test ./...`、`go vet ./...`、`go build ./...` 与定向 PostgreSQL race 测试通过。 - 真实本机数据库迁移成功:2 条既有路由保留;运行态必填列无空值;`2026081419000` 已登记;`idx_sense_media_routes_path` 唯一索引有效。 - #70 Windows 包完成重建和审计;ZIP SHA-256:`7E35394F6377F9F4AB0046E8413F29B140D6F301383C54C87CE871B435EF253D`。Web、SPA、health、MediaMTX 均返回 200,停止脚本执行后相关端口无监听。 - `git diff --check` 和任务 Wiki 定向同步检查通过。Harness strict 仅被既存 #66/#67 归档格式问题阻塞,与本任务改动无关。 - 未验证:客户全新 Windows 主机、客户生产 PostgreSQL 账号、真实获准摄像机环境仍待人工验收。 ## 人工验收(2026-08-16) - 用户明确验收通过 #95。 - PR #96 已合入 `dev`,merge commit:`12e1bff8b4af0ced2b4f040654e6440c59d60b3d`;`main` 未变更。 - Wiki 归档 revision:`5f3bdf3786f2a72dac5d29236ff466743e2912b5`;验收镜像提交:`15bd0008163704da6797214545d0cb94fc3d2e50`。
ila added the kind/taskproject/sensescope/independentpriority/p0 labels 2026-08-15 15:37:59 +08:00
Author
Owner

用户已明确指示执行 #95。前置 #92 已关闭并合入 dev;修复分支 fix/95-sense-media-path-constraint 从最新 dev@4040970 创建,工作区干净。真实库只读证据与迁移前仓库外备份均已确认;先在隔离 PostgreSQL 复现和验证,未通过前不继续写真实库。

用户已明确指示执行 #95。前置 #92 已关闭并合入 `dev`;修复分支 `fix/95-sense-media-path-constraint` 从最新 `dev@4040970` 创建,工作区干净。真实库只读证据与迁移前仓库外备份均已确认;先在隔离 PostgreSQL 复现和验证,未通过前不继续写真实库。
Author
Owner

首轮隔离回归获得预期的渐进反馈:旧 path 约束已被正确识别并越过原 SQLSTATE 42704,随后非空旧表在新增 source_ready NOT NULL 时触发 SQLSTATE 23502。测试数据库已在失败后删除。按工单保留路由与完整迁移目标,已补充确定性运行态列回填方案。

首轮隔离回归获得预期的渐进反馈:旧 path 约束已被正确识别并越过原 SQLSTATE 42704,随后非空旧表在新增 `source_ready NOT NULL` 时触发 SQLSTATE 23502。测试数据库已在失败后删除。按工单保留路由与完整迁移目标,已补充确定性运行态列回填方案。
Author
Owner

#95 已完成实现、测试、真实数据库迁移验证、Windows 包回归、Wiki 更新、归档和推送,现进入“待验收”。

  • PR:#96 #96(可合并)
  • 分支最新提交:d0185caa0e0c96ba34f10486910a79f26af57623
  • 实现提交:3dd4890
  • Task Wiki revision:89f668bc6f3308edbc1ce874c9b0a189dd418d42
  • Windows ZIP SHA-256:7E35394F6377F9F4AB0046E8413F29B140D6F301383C54C87CE871B435EF253D

工单与 PR 保持 open,等待用户人工验收;未自动合并到 dev,未关闭 #95。

#95 已完成实现、测试、真实数据库迁移验证、Windows 包回归、Wiki 更新、归档和推送,现进入“待验收”。 - PR:#96 https://git.ilapage.cn/ila/yovision/pulls/96(可合并) - 分支最新提交:`d0185caa0e0c96ba34f10486910a79f26af57623` - 实现提交:`3dd4890` - Task Wiki revision:`89f668bc6f3308edbc1ce874c9b0a189dd418d42` - Windows ZIP SHA-256:`7E35394F6377F9F4AB0046E8413F29B140D6F301383C54C87CE871B435EF253D` 工单与 PR 保持 open,等待用户人工验收;未自动合并到 `dev`,未关闭 #95。
ila closed this issue 2026-08-16 19:32:30 +08:00
Author
Owner

用户已于 2026-08-16 明确验收通过。PR #96 已合入 dev(merge commit 12e1bff8b4af0ced2b4f040654e6440c59d60b3d);Wiki 归档及镜像已更新。main 未变更。

用户已于 2026-08-16 明确验收通过。PR #96 已合入 `dev`(merge commit `12e1bff8b4af0ced2b4f040654e6440c59d60b3d`);Wiki 归档及镜像已更新。`main` 未变更。
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: ila/yovision#95