Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions DATABASE_MIGRATION_ATOMICITY_TASK.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# 数据库迁移原子性与 Prompt 外键修复任务

## 背景

第二次工程复检确认:内容实验账本迁移在事务内调用 `sqlite3.executescript()`,会隐式提交并破坏整体回滚;历史数据库通过 `ALTER TABLE` 新增的 `ai_analysis_runs.prompt_version_id` 没有新建数据库所具备的外键。

## 目标

- 让内容实验结构、校验与迁移账本处于同一事务,失败时不残留半套表或索引。
- 为已升级的历史数据库补齐 `prompt_version_id → ai_prompt_versions.id` 外键,并保持 AI Run 数据、既有索引和下游引用。
- 迁移前生成可恢复备份;数据或结构无法安全证明时 fail-closed,不猜测或删除历史记录。

## 允许修改范围

- `app/db/database.py`
- `tests/test_schema_migration_ledger.py`
- `docs/DATABASE_SCHEMA.md`
- `DEVELOPMENT_LOG.md`、`NEXT_STEPS.md` 与本任务文件

## 禁止修改范围

- 活动 SQLite、真实 Provider、Chrome Worker、发布任务和运行中服务。
- 其他历史兼容迁移的全面重写。
- 删除或自动清空无法归因的 AI Run。

## 已确定实现要求

1. 内容实验迁移不得在账本事务内使用 `executescript()`。
2. 外键修复使用新的迁移版本和 checksum,不改写已发布迁移账本。
3. 表重建时临时关闭外键仅限该迁移连接,事务提交或回滚后恢复原状态,并执行 `PRAGMA foreign_key_check`。
4. 保留 AI Run 全部规范字段、显式索引和触发器;发现未知字段、临时迁移表或孤儿 Prompt 引用时拒绝迁移。
5. 新建库与历史升级库最终都必须具有同一 `NO ACTION` Prompt 外键语义。

## 验收标准

- 故障注入拒绝创建第二张实验表后,第一张表、索引和账本均不残留;移除故障后可安全重跑。
- 模拟旧库升级后 AI Run、反馈引用和索引保留,Prompt 外键存在,外键检查为空。
- 孤儿 Prompt 引用会使迁移整体失败,原表和数据不变,账本不写入。
- 定向测试、全量测试、Ruff、Compileall、Compose 配置和 `git diff --check` 通过。

## 测试命令

```powershell
pytest -q tests/test_schema_migration_ledger.py tests/test_database_backup_service.py
pytest -q
ruff check app tests scripts
python -m compileall -q app tests scripts
git diff --check
```

## 返回格式

- 原子性与外键修复说明、故障注入和重跑证据。
- 修改文件、测试结果、分支、提交 SHA、远端 SHA 与 PR 状态。
7 changes: 7 additions & 0 deletions DEVELOPMENT_LOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -1441,3 +1441,10 @@
- 显式片段反馈改为只绑定候选的 `source_analysis_run_id`,并验证 Run 属于同一任务;来源缺失、不存在或跨任务时保留反馈但不归因,绝不回退到当前 active Run。
- Prompt 对比统一使用“官方导入时长 → 候选时长 → 输出片段源时长”的有效时长口径,官方报表时长为空时仍能计算平均观看比例。
- 定向回归由修复前 `55 passed` 增加到 `62 passed`,全量回归 `861 passed`;Ruff、Compileall、5 个 JavaScript 语法检查、20 个 PowerShell 解析检查、三套合并 Compose 配置、`pip check` 和 `git diff --check` 均通过。测试只使用临时 SQLite 和本地 mock,未调用真实 Provider、Chrome 或发布平台,也未修改活动数据库。

## 2026-08-30 数据库迁移原子性与 Prompt 外键修复

- 内容实验账本迁移不再调用会隐式提交的 `executescript()`;表、索引、结构校验和账本现在处于同一 `BEGIN IMMEDIATE` 事务,第二张表故障注入后第一张表和账本均可完整回滚并安全重跑。
- 新增独立迁移补齐历史 `ai_analysis_runs.prompt_version_id` 外键,保留 AI Run 数据、显式索引、触发器和下游反馈引用;未知字段、残留临时表或孤儿 Prompt 引用均 fail-closed。
- 正式数据库仅做只读核对:当前 39 条 AI Run、0 条孤儿 Prompt 引用、0 条现有外键异常,确认具备无损迁移前提;本阶段没有改写活动 SQLite 或重启服务。
- 迁移与备份定向回归 `28 passed`,迁移失败、重试、外键、孤儿引用与旧 AI Run 专项 `5 passed`,全量回归 `865 passed`;Ruff、Compileall、5 个 JavaScript 语法检查、20 个 PowerShell 解析检查、三套合并 Compose 配置、`pip check` 和 `git diff --check` 均通过。验证只使用隔离临时库;活动主库大小保持 `10,276,864` bytes,但文件时间被现有后台服务持续更新,因此不把 mtime 作为“未写入”证据。
8 changes: 8 additions & 0 deletions NEXT_STEPS.md
Original file line number Diff line number Diff line change
Expand Up @@ -1181,3 +1181,11 @@
3. 对历史候选提交显式反馈时,反馈应归属于候选生成时的 Prompt Run;来源无法证明的旧候选应显示为未归因,不得算到当前 Prompt。
4. 导入抖音官方作品报表后,即使报表没有视频时长,已匹配作品的 Prompt 对比仍应按候选或输出片段时长显示平均观看比例。
5. 本阶段不需要重启服务、修改活动数据库或执行真实投稿;完成自动验收后继续处理数据库迁移原子性。

## 2026-08-30 数据库迁移原子性验收

1. 先确认数据库迁移 PR 的全量测试、Windows 主机冒烟和 Docker 镜像冒烟全部通过;未经用户确认不合并。
2. 本阶段自动测试只使用临时 SQLite。正式库应用必须安排维护窗口,先核对 `workflow-before-ai-prompt-version-fk-*` 备份成功,再允许新版本首次启动执行迁移。
3. 首次启动后检查迁移账本、`PRAGMA foreign_key_list(ai_analysis_runs)`、`PRAGMA foreign_key_check`、39 条既有 AI Run 数量和反馈引用;任一不一致立即停止并使用备份回滚。
4. 不需要重新调用 AI、重新投稿或改写历史 Prompt;迁移只补结构约束,不改变内容业务数据。
5. 自动验收完成后继续补异步服务边界与页面主链回归测试。
Loading
Loading