nightly-legacy-sync-check-2026-11-02.md 9.4 KB

夜间同步核查记录 · 业务日 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