fix(scheduler): bug-1513 — rejoin crash root-caused to 3 rejoin re-push bugs, not a lib race

The "libxce reconnect heap corruption" (ASAN bad-free, free(): invalid
pointer) was NOT a libxce bug — xce_ctx_reconnect is fine. The crashes are
in the scheduler's rejoin state re-sync path (config/dataset re-push),
fixed with the reconnect/rejoin feature now integrated:

1. group.c makeGroup refresh: FREEUP(m->u.name) raw-freed shared interned
   xstrings (makeString is a global refcounted table); the correct release
   is dereference(), matching group_killer. → dereference().
2. limit.c limit_update_config: after key_add returns -EEXIST (rejoin
   re-push — limit name already present) it called kill_limit(limit), but
   xkvmgr_add's vclear already freed the limit via the kvmgr datakiller
   (=kill_limit). → skip on -EEXIST (idempotent re-push).
3. handler.c/scheduler.c: the config push was applied inline on the vcpu
   thread (sched_init_config adds queues/plugin queues) while job updates
   flow through pend_updates on the main loop — concurrent rejoin re-push
   let fcfs_update_job see a NULL j->queue (SIGSEGV). Route the config
   push through pend_updates (XCE_SCHED_OP_UPDATE_CONFIG, killer case
   already existed) and process config requests FIRST in sched_update via
   a staging queue so jobs always see their queues/resources.

Verified: 295 lib unit tests pass under ASAN (incl. 2 new group-refresh
regression tests), scheduler survives 3 consecutive controller restarts
(each rejoin ok), full job lifecycle (submit→dispatch→DONE) on the
cluster. Also fixed stale build deps (libxce_deps missing librtti_dep,
scheduler_deps missing ssl_dep/c_capnproto_dep, unity include path).

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
lei.chen 2026-08-14 07:29:32 +00:00
parent 2044dedb2f
commit 7f8c07ba85
10 changed files with 253 additions and 27 deletions

View file

@ -94,13 +94,15 @@ spec 声称 36/36 任务完成,但 re-audit(research.md)发现缺陷。来
- **apiserver rejoin**(clib.go `StartHeartbeat` + `xce_backend_rejoin`):心跳从失败转成功时显式 rejoin(HostRole_apiserver),恢复 push 通知。实测:controller 重启后日志 `backend re-joined controller after reconnect`。
- **apiserver fail-fast 电路熔断(Req 4,commit 2bbe894)**:health.go 原子熔断 + `callBackend` 包 4 个写调用。degraded 时立即返回 `backend controller unavailable`,不调 lib;传输错误或 XCE NOMASTER/REDIRECT/NOT_READY/STANDBY 触发熔断;语义错误不触发。实测:杀 controller 后第 2 次提交 ~11ms 返回 fail-fast。7 单测。
- **⚠️ 已知缺口**:**apiserver 心跳恢复在 controller 完整死亡+重启后不总是可靠**——lib transport reconnect 未稳定重建到新 controller(与 scheduler reconnect 同类,task #13)。当前需手动重启 apiserver 恢复(clean rejoin 正常)。
- **❌ scheduler reconnect 被真实 heap corruption 阻塞(未合入,已回退)**:
- scheduler 的 rejoin(`xcecli_set_reconnect_handler` flag + 主循环 `xcecli_join`)**功能上能重连并 rejoin**(实测收到 config push),但 rejoin 后 ~3-8s **崩溃**。
- **诊断(2026-08-14 深入)**:ASAN 报 `bad-free`(释放 60 字节区域偏移 56 处的指针,`...66b8`,分配线程 T4/释放线程 T5,**全栈在 loader**);**debug 非 ASAN 构建同样崩**(`free(): invalid pointer` + core,gdb 显示 malloc_printerr 后栈被破坏)——**真实堆损坏,非 ASAN 假象**。
- 尝试的修复(均无效):`xce_ctx_reconnect` 先 `iomgr_unmonitor` 再 `endpoint_fini`(对齐 xce_ctx_stop 的安全拆除,正确但不充分)。
- **候选根因**:(a) rejoin 触发 config 重推 → `sched_init_config` 非幂等重跑(sharedInst 的 MOVE_PTR/zarray_put killer 覆盖、queues/resources 重加);(b) lib reconnect 与 vtask 并发处理旧 endpoint;(c) 重新同步时 scheduler 本地状态与重推的 merge 缺陷。栈被破坏无法干净回溯,需 valgrind/隔离测试定位。
- **修法**:独立深层调试任务(scheduler 状态重同步幂等性 + lib reconnect/vtask 生命周期)。为保持 scheduler 稳定已回退全部 reconnect 改动。
- 剩余任务(scheduler reconnect + 多 controller E2E + apiserver 电路熔断等)见 spec。
- **✅ scheduler reconnect 已修复(2026-08-14 根因定位 + 集群验证)**:
- **关键结论**:所谓 "libxce reconnect heap corruption" **不是 lib 竞态**——是 scheduler rejoin 状态重同步的 3 个真实 bug(均在 config/dataset 重推路径)。
- **根因 1(group.c:234/247)**:`makeGroup` 刷新已有 group 的成员时 `FREEUP(m->u.name)` **raw-free 了共享 interned xstring**(`make_g_member` 用 `makeString` 建的是全局引用计数共享串,多个 group 共享同一指针)。正确释放是 `dereference()`(`group_killer` 就这么做)。ASAN 报 `bad-free`/`free(): invalid pointer` 即源于此。
- **根因 2(limit.c:76)**:`limit_update_config` 在 `key_add` 返回 -EEXIST(rejoin 重推,limit 名已存在)后调用 `kill_limit(limit)`,但 `xkvmgr_add` 的 vclear 已用 kvmgr datakiller(=kill_limit)释放该 limit → **double-free/UAF**(ASAN:56 字节 limit 的 READ of size 8)。修复:EEXIST 时 skip(re-push 幂等)。
- **根因 3(handler.c + scheduler.c)**:config push 在 vcpu 线程**内联**执行 `sched_init_config`(加 queues/plugin 队列),而 job 更新走 pend_updates 由主循环处理——**rejoin 重推时两者并发**,`fcfs_update_job` 的 `j->queue->name` 可能在 config 建好队列前取到 NULL → 主线程 SIGSEGV。修复:config push **改走 pend_updates**(`XCE_SCHED_OP_UPDATE_CONFIG`),`sched_update` 里 **config 请求先处理**(staging 队列)再处理 job/worker 更新,串行化顺序。
- **验证**:295 个 lib 单测全过(ASAN,含 2 个新 group 刷新回归测试);集群上 scheduler 连续 3 次 controller 重启全部存活并 `rejoin ok`;完整作业生命周期 `sleep 30` 提交→派发到 t1→**DONE**。
- **附带修复**:`libxce_deps` 缺 `librtti_dep`(group.c 编译不过)、`scheduler_deps` 缺 `ssl_dep`/`c_capnproto_dep`、unity include 只给 `<unity.h>` 路径——均已在 meson.build 修复。
- **apiserver 恢复缺口仍在(task #12)**:controller 完整死亡+重启后 apiserver 心跳恢复不可靠,电路熔断保持 degraded,需手动重启 apiserver(本次验证即遇到:job 提交报 `backend controller unavailable (reconnecting)`,重启 apiserver 后恢复)。
- 剩余任务(多 controller E2E + apiserver 自动恢复等)见 spec。
### P2.5
- **来源**:`product.md:35`、`docs/superpowers/plans/2026-05-07-p25-discovery.md`