Przeglądaj źródła

docs(migration): add nightly legacy sync check for 2026-11-03 (blocked, no server receipt)

彭峰 2 dni temu
rodzic
commit
630887ebe7
1 zmienionych plików z 136 dodań i 0 usunięć
  1. 136 0
      docs/migration/nightly-legacy-sync-check-2026-11-03.md

+ 136 - 0
docs/migration/nightly-legacy-sync-check-2026-11-03.md

@@ -0,0 +1,136 @@
+# 夜间同步核查记录 · 业务日 2026-11-03
+
+> 性质:只监测、不重复执行的核查。本轮未启动任何同步、未走同步桥、未 claim、未抢占服务端租约、未删除目录锁、未自动解决冲突、未读取或输出任何凭据内容。凭据文件仅用于 `node --env-file=` 加载。
+
+## 一、结论
+
+**业务日 2026-11-03(Asia/Shanghai)夜间同步:未完成 / 阻塞,无服务端成功回执。** 终态未出现(本轮不宣称终态),验收清单全不满足。
+
+- 真实 23:00 触发已发生:北京时间 2026-11-03 23:00:07 由 launchd 以 `--scheduled` 入口拉起,runId `5f2ddc11-0139-4356-a2f6-052e1bfb5c33`。
+- 本机主运行报告为 `blocked`,原因 `legacy_account_identity_conflict: resolve reading-generated IDs before syncing business records`,`identityConflicts=1`、`failed=1`、`datasets=[]`。
+- 服务端 `xiaoshu/sync/direct-v1` 只读 status 中该 run 仍为 `running`,`finishedAt=null`、`summary=null` —— **没有成功回执**。
+- 9 个业务数据集与财务分组**一个都未执行**,账号/栏目仅完成前置补缺扫描即被阻断,未产生业务写入与对账证据。
+- 相对上一业务日 2026-11-02(成功回执),本业务日为「由成功转为失败」的重大变化,且新增 1 处身份冲突 → **通知用户**。
+- 本轮只记录冲突,不自动解决;阻塞需人工核实处置。
+
+保留新系统修改、账户余额、课时、人工确认与复习计薪凭据的要求未被破坏:本次根本没有进入业务投影阶段。`settlementReady` 按设计始终为 `false`,未伪造。
+
+## 二、LaunchAgent 定义与实际触发
+
+| 项目 | 核查结果 |
+| --- | --- |
+| plist 路径 | `/Users/mac/Library/LaunchAgents/cn.xiaoshu.legacy-direct-sync-nightly.plist` |
+| ProgramArguments | `/usr/local/bin/node` + `--env-file=/Users/mac/.config/xiaoshu/legacy-sync-nightly.env` + `~/Library/Application Support/XiaoshuSync/scripts/run-nightly-direct-sync.mjs` + `--scheduled` —— **与预期一致** |
+| EnvironmentVariables.TZ | `Asia/Shanghai` —— **与预期一致** |
+| StartCalendarInterval | `Hour=23` / `Minute=0` —— **与预期一致** |
+| 禁用状态 | `launchctl print-disabled gui/501` 中该 job 为 **enabled**(非 11-01 的用户停运态) |
+| runs 计数 / last exit code | 23:00:53 首次读取 `runs=2 / exit=1`;23:40 收尾时 `runs=10 / exit=1`(每 5 分钟一次失败重试) |
+| launchd 状态 | `spawn scheduled`,`active count` 在重试瞬间为 1;`minimum runtime=300` |
+
+**实际触发证据(三重一致):**
+
+1. 主运行报告 `startedAt = 2026-11-03T15:00:07.605Z`(北京时间 23:00:07),`businessDate=2026-11-03`,`runId=5f2ddc11-…`;
+2. 服务端 run 记录 `startedAt = 2026-11-03T15:00:11.147Z`(北京时间 23:00:11),业务日 `2026-11-03`;
+3. `launchagent.log` 尾部出现 `{"status":"blocked","reason":"legacy_account_identity_conflict: …","runId":"5f2ddc11-…"}`,且 23:05/23:15/23:25/23:35 的 5 分钟重试报告均为 `blocked`。
+
+**环境前提:** 机器开机且该用户已登录控制台会话(`launchctl print gui/501` 可用),不是关机/未登录导致的「未运行」。这是真实 23:00 日历触发,不是唤醒补跑;Codex/聊天窗口是否关闭不影响 LaunchAgent。
+
+**`launchagent-error.log`:** 文件 mtime 为 2026-10-17,尾部 `ERR_MODULE_NOT_FOUND sync-alerts.mjs` 与「同步通知发送失败」均为 10-17 历史残留,与本轮无关;该模块当前存在。本轮真实失败原因以 `launchagent.log` 的 `blocked` 行为准。`direct-alert-state.json` 为 `{"failed":true,"lastKey":"failure","notifiedAt":1793718015856(23:00:15),"conflicts":3872,"lastSuccessAt":1793710469429(20:54:29)}` —— 告警状态由 23:00 的失败运行置位,非新的独立故障。
+
+## 三、本机私有状态目录(业务日 2026-11-03)
+
+主报告:`~/.local/state/xiaoshu/legacy-sync/2026-11-03-direct-5f2ddc11-0139-4356-a2f6-052e1bfb5c33.json`(0600)。该文件在每 10 分钟的主重试中被同一 runId 覆写,末次内容:
+
+| 项目 | 值 |
+| --- | --- |
+| businessDate / runId | `2026-11-03` / `5f2ddc11-0139-4356-a2f6-052e1bfb5c33` |
+| status | `blocked` |
+| 末次起止(UTC / 北京) | `15:40:10.906Z → 15:40:16.227Z` / `23:40:10 → 23:40:16` |
+| reason | `legacy_account_identity_conflict: resolve reading-generated IDs before syncing business records` |
+| failed / datasets | `1` / `[]`(0 个数据集) |
+| source / bridgeUsed | `old-api/WXAPP` / `false` |
+| accounts 前置扫描 | `source 750 / created 0 / matched 745 / missing 0 / conflicts 172 / excluded 4 / identityConflicts 1` |
+| nodes 前置扫描 | `source 461 / created 0 / matched 461 / missing 0` |
+| existingAccountBalancesUntouched | `true` |
+| settlementReady | 未设置(按设计为 `false`) |
+
+**5 分钟重试序列(全部 `blocked`,只记录、不干预):**
+
+| 时间(北京) | 原因 |
+| --- | --- |
+| 23:00 / 23:10 / 23:20 / 23:30 / 23:40 | `legacy_account_identity_conflict`(主 runId 报告被覆写) |
+| 23:05 / 23:15 / 23:25 / 23:35 | `another_sync_running`(并发/服务端租约占用) |
+
+`another_sync_running` 与服务端租约占用均按约束仅记录,未抢占租约、未删除目录锁。目录锁在运行器各次重试之间自行释放;23:40 观测到一次仍在运行的重试进程与短暂存在的锁目录,**未对其做任何处置**。
+
+**排除在终态判定之外的记录(不把下午/凌晨人工批次当 23 点运行):**
+
+| 文件 / runId | 时间(北京) | 状态 | 排除理由 |
+| --- | --- | --- | --- |
+| `2026-11-02-direct-22a1d53e-4943-4902-934e-85d8e0e60006.json` | 11-03 20:54 | `already_completed` | 傍晚补跑/人工批次,业务日仍为 `2026-11-02`,仅回读既有成功回执,不是当晚 23:00 运行 |
+| `12f01ee1-90b8-46d5-8f5a-73fe95515ee0` | 11-02 23:00–23:39 | `completed-with-preserved-conflicts` | 上一业务日成功回执,仅作基线比对 |
+
+## 四、服务端只读回执(仅 `action:'status'`)
+
+查询方式:`node --env-file=<凭据文件> tmp/direct-v1-status-check.mjs`(只发 `action:'status'`,不建锁、不抢租约、不启动同步;输出对 token/secret/key/password/session 类字段脱敏)。全程未读取或输出凭据内容。
+
+| 项目 | 服务端 | 本机 |
+| --- | --- | --- |
+| businessDate | `2026-11-03` | `2026-11-03` |
+| runId | `5f2ddc11-0139-4356-a2f6-052e1bfb5c33` | 同 |
+| status | `running` | `blocked` |
+| startedAt | `2026-11-03T15:00:11.147Z` | `15:00:07.605Z`(首次) |
+| finishedAt | `null` | 每轮重试有 `finishedAt`(仅表示单轮退出,非成功回执) |
+| summary | `null` | 无(datasets 为空) |
+| 9 数据集 / 财务回读 | 无 | 无 |
+
+服务端仅返回 10-29/10-30/10-31、11-01/11-02 与本次 11-03 的 run;11-03 无 `finishedAt`、无 `summary`,因此**不存在可验收的成功回执**。服务端 `status=running` 与本机 `status=blocked` 不一致,不满足「终态判定必须基于服务端回执 + 本机报告一致」的条件,故本轮不判定为终态。
+
+## 五、验收清单逐项核验(全部不满足)
+
+| # | 验收项 | 结果 | 证据 |
+| --- | --- | --- | --- |
+| 1 | 9 个业务数据集服务端业务 ID、主表与关联附表、校验和核验 | **未通过** | 9/9 未执行;无 `reconciled`、无 `sourceIdChecksum`/`targetChecksum` |
+| 2 | 每个业务编号恰有一份主表 | **未通过** | 无 `allCapturedSourceIdsReconciled`,无对账证据 |
+| 3 | 账号只新增并匹配旧用户编号 | **未通过** | 前置扫描 `identityConflicts=1`,身份冲突未解决,业务投影被中断;`created=0` 但无法核验通过 |
+| 4 | 栏目只补缺 | **未通过** | 前置扫描 `461/461 / created 0 / missing 0`,但未进入写入与回读核验 |
+| 5 | 财务分组(会员流水 stype=1/2/3/4)回读核对 | **未执行** | `datasets=[]`,无 finance 字段、无 `reconciled` |
+| 6 | `failed=0` | **未通过** | 本机每轮 `failed=1` |
+| 7 | 锁已释放 | **未通过(服务端租约仍占用)** | 服务端 run `status=running`、`finishedAt=null`;目录锁在重试间隙释放,本轮未删除任何锁 |
+| 8 | 来源 `old-api/WXAPP` 且 `bridgeUsed=false` | 元数据一致但不构成成功 | 两份报告均标注该来源与 `false`,但未走同步桥 |
+| 9 | 保留新系统修改、余额、课时、人工确认、复习计薪凭据 | 未被破坏 | `existingAccountBalancesUntouched=true`;未发生业务写入;冲突仅记录、不自动解决 |
+
+9 个数据集明细(全部 `executed=false / reconciled=false`):专项训练、词库、课程绑定、每日学习、练习记录、抗遗忘、预约、上课、测评。
+
+## 六、监测过程与终态判定
+
+- 监测窗口:23:07–23:41(北京),按 10 分钟间隔轮询 4 次(23:07 / 23:17 / 23:27 / 23:37),只读检查本机报告、服务端 status 与 launchd 计数。
+- 4 次轮询**状态零变化**:服务端始终 `running/finishedAt=null`;主报告始终 `blocked / legacy_account_identity_conflict / identityConflicts=1 / failed=1 / datasets=0`;`runs` 由 3→5→7→9(23:40 收尾时为 10),`last exit code` 恒为 1。
+- 按 2026-10-31 的既有先例(确定性阻塞项、4 次轮询零变化后定论),在 40 分钟观察窗后收尾,**未耗尽 120 分钟上限**:该阻塞需人工核实处置,自动重试不会自行解除,继续等待不改变「无成功回执」的事实。
+- 终态判定:**未出现终态**。分类 `incomplete_blocked / failure_no_success_receipt`;不宣称同步彻底失败(重试仍在进行、租约仍被持有),但按「没有服务端真实回执就报失败/未完成」如实报告未完成。
+- 失败原因已定位到实际日志:`legacy_account_identity_conflict`,新增 1 处阅读账号生成的编号与旧系统身份冲突(11-02 时 `identityConflicts=0`)。
+
+## 七、与上次状态的差异
+
+| 维度 | 2026-11-02(上次核查) | 2026-11-03(本次) |
+| --- | --- | --- |
+| 终态 | `completed-with-preserved-conflicts`(成功回执) | 未出现终态,`incomplete_blocked` |
+| 服务端回执 | `finishedAt` 非空、`summary` 完整 | `finishedAt=null`、`summary=null` |
+| 9 数据集 / 财务 | 9/9 `reconciled=true`,财务 rows 42,282 `reconciled=true` | 0/9 执行,财务未执行 |
+| failed | 0 | 1(每轮重试) |
+| 阻塞项 | 无 | `legacy_account_identity_conflict` |
+| 账号 source / conflicts / identityConflicts | 749 / 170 / 0 | 750 / 172 / **1** |
+| runs / last exit code | 2 / 0 | 10 / 1 |
+| 变化分类 | — | `success_to_failure_new_identity_conflict`,严重级别:**重大** |
+
+## 八、通知与后续
+
+- **通知:是。** 触发条件为「由成功转为失败的重大失败变化」:上一业务日取得完整成功回执,本业务日真实 23:00 触发后即被账号身份冲突阻断、9 个数据集全未执行、服务端无回执;阻塞项由 `identityConflicts=0` 变为 1,与上次状态不同。
+- **待办(需人工,非本轮执行):** 核实并处置 1 处阅读账号编号与旧系统身份冲突,可参照 2026-10-31「撤销伪编号 + 结案冲突」的非破坏性处理路径。冲突只记录、不自动解决。阻塞解除后,同一 runId 可在后续重试中续跑;届时须重新按第一节的验收清单核对服务端回执。
+- **持续风险提示:** 该任务依赖这台 Mac 开机并登录该用户;关机或未登录当日不会有运行与回执。服务端租约仍由该 runId 持有,零点后的重试可能因 `another_sync_running` 继续被阻断;工资结算所需新鲜度高于本任务的一日一次节奏,`settlementReady` 按设计始终为 `false`。
+
+## 九、交付物
+
+- 本机心跳:`~/.local/state/xiaoshu/legacy-sync/heartbeat-23-status-2026-11-03.json`(0600,含业务日、runId、终态、失败原因、9 数据集与账号/栏目补缺核验、差异、通知判定,**不含任何凭据**)
+- 本机运行报告:`~/.local/state/xiaoshu/legacy-sync/2026-11-03-direct-5f2ddc11-0139-4356-a2f6-052e1bfb5c33.json`(0600)
+- 核查记录:本文件 `docs/migration/nightly-legacy-sync-check-2026-11-03.md`