فهرست منبع

docs: record 2026-11-02 nightly legacy sync check (success receipt)

彭峰 18 ساعت پیش
والد
کامیت
2755d4753b
1فایلهای تغییر یافته به همراه126 افزوده شده و 0 حذف شده
  1. 126 0
      docs/migration/nightly-legacy-sync-check-2026-11-02.md

+ 126 - 0
docs/migration/nightly-legacy-sync-check-2026-11-02.md

@@ -0,0 +1,126 @@
+# 夜间同步核查记录 · 业务日 2026-11-02
+
+> 性质:只监测、不重复执行的核查。本轮未启动任何同步、未走同步桥、未 claim、未抢占服务端租约、未删除目录锁(锁由运行器自行释放后核验)、未读取或输出任何凭据内容。凭据文件仅用于 `node --env-file=` 加载。
+
+## 一、结论
+
+**业务日 2026-11-02(Asia/Shanghai)夜间同步:已完成,取得服务端真实成功回执,终态 `completed-with-preserved-conflicts`,`failed=0`。**
+
+- 这是**真实 23:00 触发**:进程于北京时间 2026-11-02 23:00:00 由 launchd 拉起(pid 53341),`--scheduled` 入口。
+- 服务端 `xiaoshu/sync/direct-v1` 只读 status 返回同一 runId 的终态回执,`finishedAt` 非空,与本机报告**逐数据集一致**。
+- 9 个业务数据集全部 `reconciled=true`,来源 ID 校验和与目标校验和逐一相符;财务分组回读核对通过;账号/栏目补缺符合预期;锁已释放。
+- 本次为**用户 2026-11-01 16:57 要求停用后,调度恢复运行的首次真实 23:00 触发**,属重大状态变化,按规则通知用户。
+
+保留冲突 3,872 条:这是「有意保留新系统修改」的结果,不等于源与目标不一致,也不构成失败;其中账号冲突 170 条(身份冲突 `identityConflicts=0`)。
+
+## 二、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` —— **与预期一致** |
+| launchd 状态 | 运行期 `state = running`、`pid = 53341`;收尾后 `state = not running` |
+| runs 计数 / last exit code | `runs = 2`、`last exit code = 0` |
+| 禁用状态 | `launchctl print-disabled gui/501` 中该 job 为 **enabled**(11-01 的 disabled 已被撤销) |
+
+**实际触发证据(三重一致):**
+
+1. `ps` 显示 `/usr/local/bin/node … run-nightly-direct-sync.mjs --scheduled` 于 `Mon Nov 2 23:00:00 2026` 启动,pid 53341;
+2. 目录锁 `~/.local/state/xiaoshu/legacy-sync/direct-nightly.lock/owner.json` 记录 `{"pid":53341,"startedAt":"2026-11-02T15:00:00.397Z"}`(北京时间 23:00:00);
+3. 服务端 run 记录 `startedAt = 2026-11-02T15:00:03.138Z`(北京时间 23:00:03),业务日 `2026-11-02`。
+
+**环境前提:** 机器开机且该用户已登录控制台会话,非关机/未登录导致的误判。核查窗口 23:00–23:41(北京时间),轮询间隔 5 分钟观察、10 分钟汇报,运行状态在 23:39 转为终态后收尾,无需延长至 120 分钟。
+
+## 三、本机私有状态目录(业务日 2026-11-02)
+
+主报告:`~/.local/state/xiaoshu/legacy-sync/2026-11-02-direct-12f01ee1-90b8-46d5-8f5a-73fe95515ee0.json`(0600)
+
+| 项目 | 值 |
+| --- | --- |
+| businessDate / runId | `2026-11-02` / `12f01ee1-90b8-46d5-8f5a-73fe95515ee0` |
+| status | `completed-with-preserved-conflicts` |
+| 起止(UTC / 北京) | `15:00:00.397Z → 15:39:26.413Z` / `23:00:00 → 23:39:26` |
+| failed | `0` |
+| createdBusinessRecords / refreshedBusinessRecords | 159 / 138 |
+| 保留冲突 | 3,872(含账号冲突 170) |
+| source / bridgeUsed | `old-api/WXAPP` / `false` |
+| allCapturedSourceIdsReconciled | `true` |
+| settlementReady | `false`(按设计不设置) |
+
+被排除在终态判定之外的记录(按「不把人工批次当 23 点运行」的要求):
+
+| 文件 / runId | 时间(北京) | 状态 | 排除理由 |
+| --- | --- | --- | --- |
+| `2026-11-02-direct-47ca3da6-4832-40c6-871d-a8ab94a5db9f.json` | 11-02 12:55 | `ready`,`datasets: []` | 白天人工 `--check` 批次,未执行任何数据集,不计入当晚运行 |
+| `904997ef-4062-4eb5-a50e-1a7e10cb27f7` | 11-01 23:19–23:57 | `completed-with-preserved-conflicts` | 属于业务日 2026-11-01,仅作基线比对 |
+
+日志侧证:`launchagent.log` 逐数据集分页推进正常(练习记录 450/450、抗遗忘 204/204、测评 1/1 等),收尾行 `{"status":"completed-with-preserved-conflicts","reason":"","runId":"12f01ee1-…","created":159,"updated":138,"conflicts":3872}`。`launchagent-error.log` 尾部仍为 2026-10-17 的 `ERR_MODULE_NOT_FOUND sync-alerts` 历史残留(该文件当前存在),与本轮无关。告警状态文件 `direct-alert-state.json` 为 `{"failed":false,"lastKey":"recovered","conflicts":3872,...}`。
+
+## 四、服务端只读回执(仅 `action:'status'`)
+
+查询方式:`node --env-file=<凭据文件> tmp/direct-v1-status-check.mjs`(只发 `action:'status'`,不建锁、不抢租约、不启动同步;输出对 token/secret/key/password/session 类字段脱敏)。
+
+| 项目 | 服务端 | 本机 | 一致 |
+| --- | --- | --- | --- |
+| businessDate | 2026-11-02 | 2026-11-02 | ✅ |
+| runId | `12f01ee1-90b8-46d5-8f5a-73fe95515ee0` | 同 | ✅ |
+| status | `completed-with-preserved-conflicts` | 同 | ✅ |
+| startedAt / finishedAt | `15:00:03.138Z` / `15:39:13.641Z` | `15:00:00.397Z` / `15:39:26.413Z` | ✅(起停分别为客户端与服务端落库时刻,差值为秒级) |
+| failed | 0 | 0 | ✅ |
+| 9 个数据集逐项 created/updated/conflicts/reconciled | 见下表 | 同 | ✅ |
+| 账号 / 栏目 / 财务 | 见下 | 同 | ✅ |
+
+## 五、验收清单逐项核验
+
+| # | 验收项 | 结果 | 证据 |
+| --- | --- | --- | --- |
+| 1 | 9 个业务数据集服务端业务 ID、主表与关联附表、校验和核验 | 通过 | 9/9 `reconciled=true`,`sourceIdChecksum === targetChecksum`,`createdAddon` 与主表一致 |
+| 2 | 每个业务编号恰有一份主表 | 通过 | `allCapturedSourceIdsReconciled=true`,各数据集 `sourceCount` 与 `source` 相符 |
+| 3 | 账号只新增并匹配旧用户编号 | 通过 | `source 749 / created 0 / matched 745 / missing 0 / excluded 4 / identityConflicts 0` |
+| 4 | 栏目只补缺 | 通过 | `source 461 / created 0 / matched 461 / missing 0` |
+| 5 | 财务分组(会员流水 stype=1/2/3/4)回读核对 | 通过 | `members 550 / streams 2200 / rows 42,282 / reconciled=true` |
+| 6 | `failed=0` | 通过 | 本机报告与服务端回执均为 0 |
+| 7 | 锁已释放 | 通过 | `direct-nightly.lock` 目录已消失,pid 53341 已退出,`launchd state = not running` |
+| 8 | 来源 `old-api/WXAPP` 且 `bridgeUsed=false` | 通过 | 两份回执均一致,未使用同步桥 |
+| 9 | 保留新系统修改、余额、课时、人工确认、复习计薪凭据 | 通过 | `supplement.existingAccountBalancesUntouched=true`;冲突仅记录(3,872 条),不自动解决 |
+
+数据集明细(本机与服务端一致):
+
+| 数据集 | 源条数 | 页数 | 新增 | 新增附表 | 更新 | 冲突 | failed | reconciled |
+| --- | --- | --- | --- | --- | --- | --- | --- | --- |
+| 专项训练 | 19 | 1 | 0 | 0 | 0 | 19 | 0 | ✅ |
+| 词库 | 106,520 | 107 | 0 | 0 | 0 | 166 | 0 | ✅ |
+| 课程绑定 | 1,542 | 2 | 0 | 0 | 1 | 118 | 0 | ✅ |
+| 每日学习 | 19,963 | 20 | 3 | 3 | 0 | 0 | 0 | ✅ |
+| 练习记录 | 449,942 | 450 | 121 | 121 | 63 | 536 | 0 | ✅ |
+| 抗遗忘 | 203,605 | 204 | 30 | 30 | 73 | 2,297 | 0 | ✅ |
+| 预约 | 31,799 | 32 | 2 | 2 | 1 | 564 | 0 | ✅ |
+| 上课 | 19,737 | 20 | 3 | 3 | 0 | 0 | 0 | ✅ |
+| 测评 | 206 | 1 | 0 | 0 | 0 | 0 | 0 | ✅ |
+
+## 六、与上次状态的差异
+
+| 维度 | 2026-11-01(上次核查) | 2026-11-02(本次) |
+| --- | --- | --- |
+| 终态 | `not_run` | `completed-with-preserved-conflicts` |
+| 原因 / 阻塞项 | `launchagent_disabled_by_user_request` | 无阻塞(`legacy_account_identity_conflict` 仍为 0) |
+| 服务端回执 | 无该业务日记录 | 有,`finishedAt` 非空 |
+| 保留冲突 | 不适用(未运行) | 3,872(上一业务日基线 4,279) |
+| 账号冲突 | 不适用 | 170(上一业务日 169) |
+| 变化分类 | — | `stopped_to_success_after_user_re_enable`,严重级别:**预期恢复,非故障** |
+
+补充说明:业务日 2026-11-01 在当日 23:19 由人工批次补齐(runId `904997ef…`,`failed=0`,9 数据集与财务均 reconciled),因此停运未造成 11-01 的数据缺口;本次 11-02 运行补齐了当日数据。两个业务日均有有效回执。
+
+## 七、通知与后续
+
+- **通知:是。** 触发条件为「由停运状态恢复后的首次真实 23:00 触发,并取得成功回执」,属重大状态变化,需告知用户。
+- **待办:无。** 无阻塞项、无残留锁、无需要人工介入的身份冲突。
+- **持续风险提示(非本次故障):** 该任务依赖这台 Mac 开机并登录该用户;关机或未登录当日不会产生运行,也不会有回执。工资结算所需的新鲜度要求仍高于本任务的一日一次节奏,`settlementReady` 按设计始终为 `false`。
+
+## 八、交付物
+
+- 本机心跳:`~/.local/state/xiaoshu/legacy-sync/heartbeat-23-status-2026-11-02.json`(0600,含业务日、runId、终态、9 数据集与账号/栏目/财务核验、差异、通知判定,不含任何凭据)
+- 本机运行报告:`~/.local/state/xiaoshu/legacy-sync/2026-11-02-direct-12f01ee1-90b8-46d5-8f5a-73fe95515ee0.json`(0600)
+- 核查记录:本文件 `docs/migration/nightly-legacy-sync-check-2026-11-02.md`