From d0185caa0e0c96ba34f10486910a79f26af57623 Mon Sep 17 00:00:00 2001 From: QiuSW <105186638@qq.com> Date: Sat, 15 Aug 2026 16:43:41 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E5=AE=8C=E6=88=90=E4=BB=BB=E5=8A=A1=20?= =?UTF-8?q?#95=20=E5=BE=85=E9=AA=8C=E6=94=B6=E5=BD=92=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...旧媒体路由唯一约束兼容迁移.md | 74 +++++++++++++++++++ wiki-docs.json | 4 + 2 files changed, 78 insertions(+) create mode 100644 docs/task/95-Sense旧媒体路由唯一约束兼容迁移.md diff --git a/docs/task/95-Sense旧媒体路由唯一约束兼容迁移.md b/docs/task/95-Sense旧媒体路由唯一约束兼容迁移.md new file mode 100644 index 0000000..a82c784 --- /dev/null +++ b/docs/task/95-Sense旧媒体路由唯一约束兼容迁移.md @@ -0,0 +1,74 @@ + +generated: true (请先修改 Gitea Wiki,禁止直接编辑本文件) +wiki_page: Task-95-Sense旧媒体路由唯一约束兼容迁移 +wiki_url: https://git.ilapage.cn/ila/yovision/wiki/Task-95-Sense%E6%97%A7%E5%AA%92%E4%BD%93%E8%B7%AF%E7%94%B1%E5%94%AF%E4%B8%80%E7%BA%A6%E6%9D%9F%E5%85%BC%E5%AE%B9%E8%BF%81%E7%A7%BB.- +wiki_revision: 89f668bc6f3308edbc1ce874c9b0a189dd418d42 +synchronized_at: 2026-08-15T08:43:18Z + + +# 95 Sense旧媒体路由唯一约束兼容迁移 + +- 类型:缺陷 +- 所属 Epic:#7 +- 所属 MVP / 版本:#8 +- 状态:待验收 +- 日期:2026-08-15 +- Gitea 工单:https://git.ilapage.cn/ila/yovision/issues/95 +- Wiki 页面:Task-95-Sense旧媒体路由唯一约束兼容迁移 +- Wiki revision:见本地镜像头 + +## 背景与目标 + +#92 修复进入真实旧库后,设备能力字段已成功转换为 JSONB;下一条媒体迁移因 GORM 尝试删除不存在的推导约束名 `uni_sense_media_routes_path` 而报 SQLSTATE 42704。只读核对确认旧 `sense_media_routes.path` 实际由 PostgreSQL 自动命名约束 `sense_media_routes_path_key` 保证唯一,且表中已有 2 条路由。 + +隔离回归越过约束错误后进一步确认,旧非空表缺少当前模型要求的运行态列,直接新增 `source_ready NOT NULL` 会报 SQLSTATE 23502。目标是在不删除路由、不削弱 path 唯一性、不伪造媒体已就绪的前提下完成旧表迁移。 + +## 最终方案 + +在现有 `2026081419000` 媒体迁移事务开头执行 PostgreSQL 专用兼容步骤。仅当旧表存在时取得 ACCESS EXCLUSIVE 锁,从 pg_catalog 读取包含 path 的唯一约束;只接受唯一的单列 `UNIQUE(path)`,复合、多重或冲突结构拒绝迁移并整体回滚。 + +确认结构后,把数据库实际约束名规范为 GORM 能识别和移除的名称,让 AutoMigrate 转换为模型的 `idx_sense_media_routes_path` 唯一索引。锁在整个迁移事务提交前持续有效,因此约束切换期间没有并发写入窗口。 + +兼容步骤同时为旧路由添加并回填当前模型要求的运行态列:`source_ready=false`、`failure_count=0`、`last_error_code=''`,随后设为 NOT NULL。保守初值表示服务启动后必须重新对账,不把旧路由冒充为已经就绪;`next_retry_at` 保持可空并由 AutoMigrate 建立。 + +## 修改文件 + +- `Sense/server/cmd/migrate/migration/version/2026081419000_media.go`:旧约束识别、规范化、锁表和运行态列兼容。 +- `Sense/server/cmd/migrate/migration/version/2026081419000_media_test.go`:隔离 PostgreSQL 旧表、非空路由、空库、无表、重复执行、唯一性和不安全结构回滚测试。 +- Wiki `Troubleshooting`、`docs/06-troubleshooting.md`:错误含义、备份、迁移、验证和回退步骤。 +- `wiki-docs.json`、`docs/task/95-Sense旧媒体路由唯一约束兼容迁移.md`:任务归档登记和镜像。 + +## 验收结果 + +| 验收标准 | 结果 | +|---|---| +| 旧约束名不再触发 SQLSTATE 42704 | 通过;隔离和真实 PostgreSQL 均完成媒体迁移 | +| 既有媒体路由完整保留 | 通过;真实库迁移前后均为 2 条 | +| path 始终具有唯一性保护 | 通过;迁移后 `idx_sense_media_routes_path` 唯一索引有效,重复 path 写入被拒绝 | +| 无表、新库、已迁移库和重复兼容 | 通过 | +| 不安全结构拒绝并回滚 | 通过;复合 path 约束夹具未发生部分变更 | +| Go 全量与隔离 PostgreSQL 回归 | 通过 | +| #70 Windows 包真实迁移与启动 smoke | 通过;Web/SPA/health/MediaMTX 200,停止后端口清空 | +| Wiki 镜像与任务归档一致 | 通过 | + +## 测试 + +- 隔离 PostgreSQL 17 `TestMediaMigrationOnPostgres`:旧 2 路由、旧约束、运行态回填、空库、无表、重复兼容、唯一性冲突和复合约束回滚全部通过;独立测试数据库每次运行后删除。 +- `go test ./...`、`go vet ./...`、`go build ./...`:通过。 +- `go test -race ./cmd/migrate/migration/version -run TestMediaMigrationOnPostgres -count=1`:使用隔离 PostgreSQL 通过。 +- #70 固定 Go 1.26.5、Node 22.22.1、pnpm 9.15.1 production build 与包审计通过。 +- 迁移前 PostgreSQL custom-format 备份通过 `pg_restore --list`;真实迁移完成,2 条旧路由保留、运行态列无空值、媒体迁移版本登记、唯一索引有效。 +- Web 首页、SPA、`/healthz` 与 MediaMTX Control API 均返回 200;`stop-sense.bat` 后相关端口无监听。 +- `git diff --check`:通过。 +- **未验证部分**:尚未在客户全新 Windows 主机、客户生产 PostgreSQL 账号和真实获准摄像机上验收;当前回归使用本机 PostgreSQL、脱敏业务计数和已配置测试摄像机环境。Harness strict 仍只受既存 #66/#67 归档格式影响。 + +## 遗留问题 + +- PowerShell `Invoke-WebRequest` 在本机受代理环境影响,访问 loopback 时失败;明确绕过代理的本机 HTTP 请求验证服务正常,不属于 Sense 服务端故障。 +- #95 需先经用户验收并合入 `dev`,随后 #70 才能按依赖顺序完成合并。 + +## 相关提交 + +- `3dd4890` 兼容旧媒体路由约束和运行态列迁移。 +- `6257859` 记录旧媒体路由迁移排错。 +- `c088caf` #70 集成 #95 后用于 Windows 包真实回归。 diff --git a/wiki-docs.json b/wiki-docs.json index cf70f4f..d18feb6 100644 --- a/wiki-docs.json +++ b/wiki-docs.json @@ -131,6 +131,10 @@ { "page": "Task-69-Sense多边形区域与方向警戒线配置", "path": "docs/task/69-Sense多边形区域与方向警戒线配置.md" + }, + { + "page": "Task-95-Sense旧媒体路由唯一约束兼容迁移", + "path": "docs/task/95-Sense旧媒体路由唯一约束兼容迁移.md" } ] }