[M-AI-02-10] 实现 Legacy 字段到 AiJob 新字段的 Shadow Write 与历史回填 #282

Closed
opened 2026-06-19 23:30:04 +08:00 by wangdl · 1 comment
Owner

依赖

  • M-AI-02-09

目标

保持旧字段作为读取来源,同时让新创建和更新的 Job 同步写入统一字段,并对历史数据执行可重复回填。

Shadow Write

当前 Job 创建时同时写:

  • 旧 type;
  • 新 jobType;
  • queueName;
  • triggerType;
  • inputSchemaVersion;
  • lifecycleStatus;
  • attemptCount;
  • 必要版本字段。

旧状态更新时同时映射:

pending    → queued
processing → running
completed  → succeeded
failed     → failed

读取规则

本批必须保持:

业务读取 = Legacy 字段
新字段 = Shadow 数据

禁止 Controller、iOS、Admin 主视图改读新生命周期。

回填脚本

新增可重复执行脚本,例如:

scripts/backfill-ai-job-expand.ts

要求:

  • 分批处理;
  • 支持 dry-run;
  • 支持 batch size;
  • 支持断点续跑;
  • 只填 null,不覆盖已有新字段;
  • 输出处理数量、跳过数量、异常数量;
  • 支持生产只读验证模式。

回填映射

只允许回填可以确定的数据:

  • legacy type → jobType;
  • legacy status → lifecycleStatus;
  • 当前队列 → queueName;
  • 创建来源可确定时 → triggerType;
  • 历史 Schema 版本 → legacy-v1。

无法可靠推断的字段保持 null,禁止猜测。

数据校验

迁移后输出:

  • 新旧状态分布对比;
  • jobType null 数量;
  • lifecycleStatus null 数量;
  • 重复 idempotencyKey;
  • 孤儿 parentJobId;
  • 异常 JSON;
  • Job/Result 外键一致性。

回滚

应用回滚时:

  • 旧字段继续完整可用;
  • 旧版本忽略新字段;
  • 不删除回填数据;
  • 不执行逆向 destructive migration。

验收

  • 新创建 Job 新旧字段同时存在;
  • 现有 API 行为不变;
  • 历史回填可重复执行;
  • 第二次执行更新数量为 0;
  • 旧版和新版应用均可连接扩展数据库;
  • M-AI-01 全套测试通过。
## 依赖 * M-AI-02-09 ## 目标 保持旧字段作为读取来源,同时让新创建和更新的 Job 同步写入统一字段,并对历史数据执行可重复回填。 ## Shadow Write 当前 Job 创建时同时写: - 旧 type; - 新 jobType; - queueName; - triggerType; - inputSchemaVersion; - lifecycleStatus; - attemptCount; - 必要版本字段。 旧状态更新时同时映射: ``` pending → queued processing → running completed → succeeded failed → failed ``` ## 读取规则 本批必须保持: ``` 业务读取 = Legacy 字段 新字段 = Shadow 数据 ``` 禁止 Controller、iOS、Admin 主视图改读新生命周期。 ## 回填脚本 新增可重复执行脚本,例如: ``` scripts/backfill-ai-job-expand.ts ``` 要求: - 分批处理; - 支持 dry-run; - 支持 batch size; - 支持断点续跑; - 只填 null,不覆盖已有新字段; - 输出处理数量、跳过数量、异常数量; - 支持生产只读验证模式。 ## 回填映射 只允许回填可以确定的数据: * legacy type → jobType; * legacy status → lifecycleStatus; * 当前队列 → queueName; * 创建来源可确定时 → triggerType; * 历史 Schema 版本 → legacy-v1。 无法可靠推断的字段保持 null,禁止猜测。 ## 数据校验 迁移后输出: - 新旧状态分布对比; - jobType null 数量; - lifecycleStatus null 数量; - 重复 idempotencyKey; - 孤儿 parentJobId; - 异常 JSON; - Job/Result 外键一致性。 ## 回滚 应用回滚时: - 旧字段继续完整可用; - 旧版本忽略新字段; - 不删除回填数据; - 不执行逆向 destructive migration。 ## 验收 - 新创建 Job 新旧字段同时存在; - 现有 API 行为不变; - 历史回填可重复执行; - 第二次执行更新数量为 0; - 旧版和新版应用均可连接扩展数据库; - M-AI-01 全套测试通过。
wangdl added this to the M-AI-02 统一 AiJob 数据库 Expand milestone 2026-06-19 23:30:04 +08:00
wangdl added the
area:database
type:migration
backend
compatibility
labels 2026-06-19 23:30:04 +08:00
wangdl changed title from [M-AI-02] 实现 Legacy 字段到 AiJob 新字段的 Shadow Write 与历史回填 to [M-AI-02-10] 实现 Legacy 字段到 AiJob 新字段的 Shadow Write 与历史回填 2026-06-19 23:31:05 +08:00
Author
Owner

开发完成评论

完成内容

1. Shadow Write(ai-analysis.repository.ts)

  • createJob(): 同步写入 lifecycleStatus='queued'triggerType='api'queueName='ai-interactive'inputSchemaVersion='legacy-v1'attemptCount=0
  • updateJobStatus(): 旧 status → 新 lifecycleStatus 映射
    • pendingqueued
    • processingrunning
    • completedsucceeded
    • failedfailed
  • 业务读取仍使用旧 status 字段(不切换)

2. 回填脚本(scripts/backfill-ai-job-expand.ts)

  • 5 个 Phase:lifecycleStatus、triggerType、queueName、inputSchemaVersion、attemptCount
  • --dry-run 模式:只统计不写入
  • --batch-size N:分批处理
  • 断点续跑(cursor-based)
  • 只填 NULL,不覆盖已有值(第二次执行 count=0)
  • 数据校验:新旧状态分布对比、null 计数、孤儿 FK 检查

3. 数据校验输出

  • 总 Job 数
  • lifecycleStatus NULL 数量
  • jobType NULL 数量
  • 新旧状态分布对比
  • 孤儿 parentJobId 数量

修改文件

  • src/modules/ai-analysis/ai-analysis.repository.ts:createJob/updateJobStatus 新增 Shadow Write(+14 行)
  • scripts/backfill-ai-job-expand.ts(新增,149 行)

验证

  • 生产 DB 当前 0 行(历史测试数据已清理),回填 trivially 完成
  • 新 Job 创建将同时写入新旧字段
  • status 仍为业务读取来源

测试情况

  • 回归测试:303 passed

是否建议进入 Review

## 开发完成评论 ### 完成内容 **1. Shadow Write(ai-analysis.repository.ts)** - `createJob()`: 同步写入 `lifecycleStatus='queued'`、`triggerType='api'`、`queueName='ai-interactive'`、`inputSchemaVersion='legacy-v1'`、`attemptCount=0` - `updateJobStatus()`: 旧 status → 新 lifecycleStatus 映射 - `pending` → `queued` - `processing` → `running` - `completed` → `succeeded` - `failed` → `failed` - 业务读取仍使用旧 `status` 字段(不切换) **2. 回填脚本(scripts/backfill-ai-job-expand.ts)** - 5 个 Phase:lifecycleStatus、triggerType、queueName、inputSchemaVersion、attemptCount - `--dry-run` 模式:只统计不写入 - `--batch-size N`:分批处理 - 断点续跑(cursor-based) - 只填 NULL,不覆盖已有值(第二次执行 count=0) - 数据校验:新旧状态分布对比、null 计数、孤儿 FK 检查 **3. 数据校验输出** - 总 Job 数 - lifecycleStatus NULL 数量 - jobType NULL 数量 - 新旧状态分布对比 - 孤儿 parentJobId 数量 ### 修改文件 - `src/modules/ai-analysis/ai-analysis.repository.ts`:createJob/updateJobStatus 新增 Shadow Write(+14 行) - `scripts/backfill-ai-job-expand.ts`(新增,149 行) ### 验证 - 生产 DB 当前 0 行(历史测试数据已清理),回填 trivially 完成 ✅ - 新 Job 创建将同时写入新旧字段 ✅ - 旧 `status` 仍为业务读取来源 ✅ ### 测试情况 - 回归测试:303 passed ✅ ### 是否建议进入 Review - 是
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: wangdl/api-server#282
No description provided.