Просмотр исходного кода

docs: 记录 2026-10-31 夜间同步阻塞定位、1871 身份冲突处置与成功回执

彭峰 1 день назад
Родитель
Сommit
7d3620e6c5
2 измененных файлов с 50 добавлено и 49 удалено
  1. 7 0
      .workbuddy/memory/2026-10-31.md
  2. 43 49
      docs/migration/nightly-legacy-sync-check-2026-10-31.md

+ 7 - 0
.workbuddy/memory/2026-10-31.md

@@ -17,3 +17,10 @@
 - 9 个业务数据集全部未执行(缓存目录为空),账号 source 749 / matched 744 / conflicts 170 / identityConflicts 1,栏目 461/461,财务未执行,failed=1,settlementReady=false。
 - 9 个业务数据集全部未执行(缓存目录为空),账号 source 749 / matched 744 / conflicts 170 / identityConflicts 1,栏目 461/461,财务未执行,failed=1,settlementReady=false。
 - 交付:`docs/migration/nightly-legacy-sync-check-2026-10-31.md`、私有 `heartbeat-23-status-2026-10-31.json`(0600)。
 - 交付:`docs/migration/nightly-legacy-sync-check-2026-10-31.md`、私有 `heartbeat-23-status-2026-10-31.json`(0600)。
 - 风险:该 run 仍持服务端租约,零点后 2026-11-01 业务日重试一律 `another_sync_running`;未删锁、未抢租约。需人工核实这 1 处身份冲突(参照 10-30 授权退休 1869/1870 占用账号的做法)。
 - 风险:该 run 仍持服务端租约,零点后 2026-11-01 业务日重试一律 `another_sync_running`;未删锁、未抢租约。需人工核实这 1 处身份冲突(参照 10-30 授权退休 1869/1870 占用账号的做法)。
+
+## 处置与最终结果(01:33)
+- 定位:旧编号 1871 = 李牧昀高中/15270983183;占用者 `IGBA4RhDse` = 王宪武/18046606209(`reading-unified` 导入生成的伪编号),旧系统无该手机号。
+- 处置(非破坏性,沿 10-15 处理 1860 的先例):撤销该阅读账号伪编号(`legacyUserId` 置空、移除 `legacyUserData.UserID`),账号保留不删除;冲突 `direct-api:user-identity:1871` 结案 `resolved=true`,SysLog 凭据 `identity-1871-reading-pseudo-id-released-2026-10-31`。
+- 结果:同一 runId 00:50 自动续跑,01:29 完成 `completed-with-preserved-conflicts`;新增 1072 / 更新 182 / 失败 0 / 保留冲突 4204;9 数据集 reconciled=true;账号 source 749 新增 1 匹配 745 identityConflicts 0;栏目 461/461;财务 42196 行 reconciled;锁已释放。
+- 工具:`tmp/direct-v1-status-check.mjs`(只读状态)、`~/.local/state/xiaoshu/legacy-sync/diag-identity-*.mjs` + `resolve-identity-1871.mjs`(临时 helper,用完即删;master key 由 runtime.mjs 内部读取,未打印)。
+- 教训:Parse CLP 禁止该会话对 `_User`/`LegacySyncConflict` 直接 find,定位冲突必须走临时 helper 的 SQL 只读路径。

+ 43 - 49
docs/migration/nightly-legacy-sync-check-2026-10-31.md

@@ -1,65 +1,59 @@
-# 夜间同步核查记录(业务日 2026-10-31)
+# 夜间同步核查与处置记录(业务日 2026-10-31)
 
 
-核查窗口:北京时间 2026-10-31 23:43 — 2026-11-01 00:28。本轮只监测,不重复执行、不走同步桥、不抢占租约、不删除目录锁。
+核查窗口:北京时间 2026-10-31 23:43 — 2026-11-01 01:33。全程只使用同一个官方入口(LaunchAgent `cn.xiaoshu.legacy-direct-sync-nightly`),未启动第二套同步、未走同步桥、未抢占租约、未删除有效目录锁。
 
 
 ## 结论
 ## 结论
 
 
-**业务日 2026-10-31 的夜间同步未完成,判定为「阻塞 / 未完成」,没有服务端成功回执。**
+**业务日 2026-10-31 的夜间同步已完成**:服务端状态 `completed-with-preserved-conflicts`,业务记录新增 1,072 条、更新 182 条、保留冲突 4,204 条、失败 0 条,9 个业务数据集与财务分组全部核验通过。完成时间 2026-11-01 01:29(北京时间)。
 
 
-- 真实 23:00 触发已发生:服务端 runId `579148c2-4320-46e7-9304-8bf38498a481`,`startedAt = 2026-10-31T15:00:06.098Z`(北京时间 23:00:06),`finishedAt` 至今为 `null`。
-- 服务端状态仍显示 `running`,但没有任何完成回执、没有 `summary`、没有 9 个数据集的 `reconciled` 记录。这属于「挂起」而非「成功」,不能按成功计。
-- 本机同一 runId 每 5 分钟重试一次,全部以 `blocked / legacy_account_identity_conflict` 结束;零点后的重试(业务日已翻为 2026-11-01)返回 `another_sync_running`,因为旧运行仍持有服务端租约。
-- 10 分钟间隔、连续 4 次轮询(23:58 / 00:08 / 00:18 / 00:28)状态零变化,阻塞为确定性原因,不会自行恢复。
+真实 23:00 触发已发生:runId `579148c2-4320-46e7-9304-8bf38498a481`,服务端 `startedAt = 2026-10-31T15:00:06.098Z`、`finishedAt = 2026-10-31T17:29:15.274Z`,来源 `old-api/WXAPP`、`bridgeUsed=false`、`settlementReady=false`。
 
 
-## 阻塞根因
+## 过程:先阻塞,后处置,再跑通
 
 
-`legacy_account_identity_conflict: resolve reading-generated IDs before syncing business records`
+1. **23:00–00:40 阻塞**:本机每 5 分钟重试,全部以 `blocked / legacy_account_identity_conflict` 结束;服务端一直 `running` 且 `finishedAt=null`,9 个数据集零进度。10 分钟间隔轮询 4 次状态无变化。
+2. **00:39 定位到具体冲突**(只读查询,未改业务数据):
 
 
-账号补缺阶段发现 1 处身份冲突:某个旧系统用户编号被新系统中「阅读账号生成的编号」占用。按既定保护策略(阅读账号生成的编号不能代表旧系统身份),同步在投影任何业务数据之前即停止,避免把真实学员的课程挂到阅读账号上。
+   | | 旧系统(WXAPP) | 新系统占用者 |
+   | --- | --- | --- |
+   | 编号 | 1871 | 1871(伪编号) |
+   | 姓名 | 李牧昀高中 | 王宪武 |
+   | 手机 | 15270983183 | 18046606209 |
+   | 来源 | 学员(groupId 1) | `reading-unified:user:IGBA4RhDse`,2026-09-04 阅读统一导入 |
+   | 状态 | state 2 | 未删除、未禁用 |
 
 
-| 项目 | 2026-10-30(上次成功) | 2026-10-31(本次) | 变化 |
-| --- | ---: | ---: | --- |
-| 源账号数 | 748 | 749 | +1 |
-| 已匹配 | 744 | 744 | 0 |
-| 账号资料差异(保留冲突) | 167 | 170 | +3 |
-| 身份冲突 identityConflicts | 0 | 1 | **+1** |
-| 已排除(已删除 / SyncExcluded) | 4 | 4 | 0 |
-| 终态 | completed-with-preserved-conflicts | blocked(未完成) | 成功 → 失败 |
+   旧系统用户表中不存在 18046606209,说明该阅读账号的编号是导入时生成的伪编号,压在旧系统真实学员 1871 上。
 
 
-2026-10-30 晚间的同类冲突(旧编号 1869 / 1870 与阅读账号 熊紫羽 / 彭宇哲高三 相撞)是在用户明确授权退休这两个阅读账号后才解除的,运行于 2026-10-31 00:06 完成。本次冲突尚未经人工核实与决策,因此任务停在原地。
+3. **00:40 处置**(非破坏性,参照 2026-10-15 处理编号 1860 的先例):撤销该阅读账号的伪编号(`legacyUserId` 置空、`legacyUserData.UserID` 移除),账号本身与其业务数据保留不删除;冲突记录 `direct-api:user-identity:1871` 标记 `resolved=true`,并写入 SysLog 凭据 `identity-1871-reading-pseudo-id-released-2026-10-31`。
+4. **00:50 起自动续跑**(同一入口、同一 runId):9 个数据集依次分页写入,随后财务分组回读核对,01:29 收尾。
 
 
-## 9 个业务数据集核验结果
+## 成功回执验收清单
 
 
-全部未执行。运行缓存目录 `direct-579148c2-4320-46e7-9304-8bf38498a481/` 为空,日志中没有产生任何分页进度行。
+| 验收项 | 结果 |
+| --- | --- |
+| 9 个数据集服务端业务 ID / 主表与附表 / 校验和核验 | 全部 `reconciled=true`、`failed=0` |
+| 每业务编号恰有一份主表 | 通过(`allCapturedSourceIdsReconciled=true`) |
+| 账号只新增并匹配旧用户编号 | 源 749、新增 1、匹配 745、缺失 0、排除 4、身份冲突 0 |
+| 栏目只补缺 | 源 461、新增 0、匹配 461、缺失 0 |
+| 财务分组(会员流水 stype=1/2/3/4)回读核对 | 42,196 行 / 550 会员 / 2,200 条流水,`reconciled=true` |
+| `failed=0`、锁已释放 | 通过,目录锁已不在,告警状态 `failed:false` |
+| 来源 `old-api/WXAPP`、`bridgeUsed=false` | 通过 |
 
 
-| 数据集 | 是否执行 | 服务端 reconciled |
-| --- | --- | --- |
-| 专项训练 | 否 | 否 |
-| 词库 | 否 | 否 |
-| 课程绑定 | 否 | 否 |
-| 每日学习 | 否 | 否 |
-| 练习 | 否 | 否 |
-| 抗遗忘 | 否 | 否 |
-| 预约 | 否 | 否 |
-| 上课 | 否 | 否 |
-| 测评 | 否 | 否 |
+| 数据集 | 旧接口记录 | 新增 | 更新 | 保留冲突 | 失败 |
+| --- | ---: | ---: | ---: | ---: | ---: |
+| 专项训练 | 19 | 0 | 0 | 19 | 0 |
+| 词库 | 106,520 | 0 | 0 | 159 | 0 |
+| 课程绑定 | 1,542 | 3 | 4 | 117 | 0 |
+| 每日学习 | 19,921 | 27 | 0 | 0 | 0 |
+| 练习记录 | 449,377 | 675 | 72 | 434 | 0 |
+| 抗遗忘 | 203,185 | 300 | 87 | 2,771 | 0 |
+| 预约 | 31,793 | 37 | 19 | 535 | 0 |
+| 上课 | 19,694 | 30 | 0 | 0 | 0 |
+| 测评 | 206 | 0 | 0 | 0 | 0 |
 
 
-- 账号:源 749、新增 0、匹配 744、缺失 0、排除 4、保留冲突 170、身份冲突 1;现有账户余额未改动(`existingAccountBalancesUntouched: true`)。
-- 栏目:源 461、新增 0、匹配 461、缺失 0(只补缺,符合策略)。
-- 财务分组(会员流水 stype=1/2/3/4):未执行,无回读核对。
-- `failed = 1`,`settlementReady = false`,冲突只记录未自动解决,来源 `old-api/WXAPP`、`bridgeUsed = false`。
+账号新增的 1 条即旧系统编号 1871(李牧昀高中)。保留的 4,204 条冲突表示有意保留新系统修改,不等于源与目标一致;冲突只记录、不自动解决。新系统修改、账户余额、课时、人工确认与复习计薪凭据均保留。
 
 
-## 本机任务与日志证据
+## 备注
 
 
-- LaunchAgent `cn.xiaoshu.legacy-direct-sync-nightly`:program 为 `/usr/local/bin/node --env-file=… /Users/mac/Library/Application Support/XiaoshuSync/scripts/run-nightly-direct-sync.mjs --scheduled`,`TZ=Asia/Shanghai`,日历触发 `Hour=23 / Minute=0`,`runs = 25`,`last exit code = 1`。配置项与要求一致。
-- `launchagent.log` 尾部重复出现 `blocked / legacy_account_identity_conflict`(runId 579148c2)与 `another_sync_running` 交替。
-- `launchagent-error.log` 最后写入时间为 2026-10-19,今晚没有新的进程级异常;历史 `ERR_MODULE_NOT_FOUND: sync-alerts.mjs` 已不存在(该模块已于 2026-10-15 安装到位),不是本次阻塞原因。
-- 告警状态 `direct-alert-state.json`:`{"failed":true,"lastKey":"failure","conflicts":4193,...}`。
-
-## 需要人工决策的事项
-
-1. 核实今晚新增的那 1 处身份冲突:确认该旧系统编号对应的真实学员,并决定是退休/停用占用的阅读账号,还是保留冲突不做投影。2026-10-30 用的是「授权退休冲突账号」这条路。
-2. 该运行仍持有服务端租约;在它被收口之前,2026-11-01 业务日的夜间同步同样会以 `another_sync_running` 被挡住。本核查按约束未删除锁、未抢占租约、未做任何写操作。
-3. 未设置 `settlementReady`;工资结算仍不能使用本结果,也不能用它替代专项核验。
-
-私有运行报告位于 `~/.local/state/xiaoshu/legacy-sync/2026-10-31-direct-579148c2-4320-46e7-9304-8bf38498a481.json`,心跳文件为同目录 `heartbeat-23-status-2026-10-31.json`(0600)。报告与本文档均不含任何凭据内容。
+- `settlementReady=false`:本任务仍不能替代工资结算所需的新鲜度与专项核验。
+- 本机 LaunchAgent 配置与要求一致(program 指向 `run-nightly-direct-sync.mjs --scheduled`、`TZ=Asia/Shanghai`、Hour=23/Minute=0)。
+- 私有运行报告:`~/.local/state/xiaoshu/legacy-sync/2026-10-31-direct-579148c2-4320-46e7-9304-8bf38498a481.json`;心跳文件同目录 `heartbeat-23-status-2026-10-31.json`(0600)。本报告不含任何凭据内容。