Commit graph

17 commits

Author SHA1 Message Date
lei.chen
ab00c08faa docs: mark P1 PEND-reason cluster verification done (070e9992)
Full job cluster verification on 2026-09-01 (HEAD a34p5yk) confirmed the
P1 reason restore end-to-end: TIME_WINDOW (night queue, window closed) and
HOST_UNAVAIL (all workers stopped) both surface via the jobs API; worker
restart without scheduler restart recovers dispatch (bug-1511 concern
disproven). Also records the third scheduler-framework merge regression
(pending_reason dropped from JOB_DEFAULT_COLUMN, fixed in 070e9992) and the
apiserver OPCode_notify-not-delivered note.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-01 15:26:52 +00:00
lei.chen
91c3fb7f04 feat(scheduler): restore PEND-reason P1 + fix dispatchwindow merge regressions
The scheduler-framework merge (2026-08-25) silently regressed the PEND
reason P1 work (4e2ac86/ee79179): the old fcfs.c became dead-on-default
(builtin_fcfs replaced it), resource.c/limit.c conflict resolution took
Rongsong's side, and the merge-fix only re-added the dispatchwindow FIELD
without the logic. After the merge, sched_queue_is_ready/sched_job_is_ready
were `return 1` stubs and only MATCH_FAIL/NO_RESOURCE were ever emitted.

Restore P1 on the active (framework) path, keeping every reason attach in
the allocator thread (bug-1521: the picker thread must not write
job->schedobj->reason — it races the allocator's MOVE_PTR):

- core.c: sched_job_is_ready (PEND & not suspended) and sched_queue_is_ready
  (QUEUE_STAT_CLOSED/INACT_ADMIN/INACT_WINDOW gate) real implementations.
- scheduler.c: allocator loop injects sched_queue_status_reason() — queue
  status first, then the dispatch window evaluated against the core's
  virtual clock (xce_core_now/1e9) so live and sim modes agree; attaches
  QUEUE_CLOSED/DRAINING/TIME_WINDOW and pends the job. Adds <time.h>.
- resource.c: skip UNAVAIL/CLOSED/CLOSED_BY_ADMIN workers + HOST_UNAVAIL
  (restored from 4e2ac86).
- limit.c: attach LIMIT_GLOBAL/LIMIT_HOST + carry reason in
  limit_job_election (restored from 4e2ac86).

Two further merge regressions found during cluster verification (the
framework core path never carried the queue dispatch window):

- core_events.c core_build_queue: duplicate q->dispatchwindow_config onto
  xce_sched_queue_t.dispatchwindow (make_sched_queue did this; the core path
  dropped it, so TIME_WINDOW could never fire on the default path).
- core_events.c core_upsert_queue update branch: refresh dispatchwindow on
  QUEUE_UPDATE.

Regression test: test_core_ready_gates.c (white-box #include of core.c,
7 cases covering both gates), registered in scheduler testsuite meson.build.

Cluster-verified on t1/t2/t3 (nix xcecore closure + AppImage standard
deployment): night queue (window closed) -> main_reason=8 time_window;
all workers down -> main_reason=9 host_unavail with per-host detail;
closed queue -> job stays PEND; normal queue baseline -> DONE. Scheduler
suite 52/53 (1 pre-existing admin-request symlink-escape failure, unrelated).

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-08-30 12:06:10 +00:00
lei.chen
6020015fa7 feat(mcp-server): add scheduler_checkpoint + scheduler_restart tools
Expose two backend capabilities MCP previously omitted:
- scheduler_checkpoint: control action "checkpoint" (CRIU)
- scheduler_restart: POST /xce/v1/jobs/:id/restart (re-queues as a new
  job, resuming from the last checkpoint if one exists; returns new jobid)

schedulerclient gains CheckpointJob/RestartJob (RestartJobResult,
JobRestartRequest); Client interface updated. Both tools marked
destructive (confirm required). tools/list now reports 17 tools.

Unit tests added/updated in both modules — all pass. Backend endpoints
are existing apiserver routes; no backend changes in this commit.

Also syncs left_scheduler_work.md §八.4 (checkpoint/restart done; the
only remaining MCP tool gap is scheduler_modify, blocked on a controller
job-field-update opcode that does not exist yet).

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-08-30 10:36:54 +00:00
lei.chen
0e9609af6d docs: record PEND reason P0+P1 implementation status (4e2ac86) 2026-08-14 09:43:44 +00:00
lei.chen
62778930e2 docs: mark apiserver auto-rejoin note as fixed (e505066) — task #12 done 2026-08-14 08:05:20 +00:00
lei.chen
e505066162 fix(apiserver): auto-recover after full controller death+restart (task #12)
The apiserver stayed circuit-breaker-degraded after a full controller
restart until manually restarted. Two root causes:

1. Breaker recovery was decoupled from the breaker itself. The breaker can
   be tripped by a backend write failure (e.g. E_XCE_STANDBY -10014 while
   the controller is electing) with no heartbeat ever failing, but
   StartHeartbeat only recovered on a fail→success heartbeat transition
   (prevFailed), so the breaker stayed degraded forever.
   → extract needsRejoinAfterHeartbeat(hbOK, prevFailed): recover when
   prevFailed OR the breaker is currently tripped. 5 new unit tests.

2. The 30s heartbeat's fire-and-forget reconnect only fires when the lib's
   internal ctx state is XCE_CTX_ERR. After a controller restart whose
   election outlasts the FAILOVER_CALL retry budget, the retry loop's
   xcecli_ctx_recover leaves the ctx RUN on a half-open connection, so the
   FF path never reconnects and the client stays disconnected forever.
   → lib: new xcecli_force_reconnect() forces a rotate+recover regardless
   of state; apiserver: 2s recoveryLoop force-reconnects + rejoins while
   degraded.

Verified on cluster: after controller kill+restart, the apiserver logs
"backend re-joined controller after reconnect" and job submissions resume
without a manual restart; a fresh job runs to DONE. Controller, scheduler
(rejoin ok, bug-1513) and workers all auto-recover.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-14 08:01:35 +00:00
lei.chen
7f8c07ba85 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>
2026-08-14 07:29:32 +00:00
lei.chen
2044dedb2f docs: task 12 — apiserver circuit breaker done (2bbe894), recovery gap noted 2026-08-14 05:21:07 +00:00
lei.chen
d4b98aab01 docs: scheduler reconnect — confirmed real heap corruption (ASAN + debug both crash)
Deep-dive: the rejoin works but crashes ~3-8s later with a REAL heap corruption
(ASAN bad-free freeing a pointer 56B into a 60B region, all-loader frames;
non-ASAN debug build aborts with free(): invalid pointer, gdb stack corrupted).
The endpoint-unmonitor fix (correct, matches xce_ctx_stop) did not help.
Candidate roots: config re-push re-running sched_init_config non-idempotently
(sharedInst MOVE_PTR/zarray_put), lib reconnect vs in-flight vtask, or
state-resync merge. All scheduler reconnect changes reverted to keep the
scheduler stable; this is a dedicated deep-debugging task.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-14 03:09:04 +00:00
lei.chen
5d345d86b5 docs: failover-adoption — worker+apiserver done (2165b1a), scheduler blocked by lib race 2026-08-14 02:35:34 +00:00
lei.chen
6f520a677b docs: record PEND reason classification as unfinished work (pending unified analysis)
Adds the PEND reason taxonomy (docs/pend-reason-investigation-design.md) to
left_scheduler_work 八.4 推迟/未决, with the three open decisions for unified
analysis: code strategy (reuse 3 codes + subcodes vs expand PREASON_* to ~20),
wiring priority (P0 main_reason + limit attach), and bjobs -p-equivalent MCP
capability.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-14 01:12:14 +00:00
lei.chen
7b7294dd1e docs: record bug-1512 multi-worker over-dispatch fix + wolf bookkeeping
- left_scheduler_work.md: bug-1494 子问题三(bug-1512)已修复,多 worker 8/8/8 均匀分布验证
- buglog.json: bug-1512 entry(controller code=0 成功 ack 被误判为失败)
- cerebrum.md: Do-Not-Repeat — notify 带 outcome code 字段时需按 controller 侧实际值做 gate
- memory.md: session log

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-13 06:22:00 +00:00
lei.chen
8871f7e204 docs: update status — Phase 2 done, human jobid, priorities (2026-08-13)
- left_scheduler_work.md: Phase 2 implemented (SSE+WatchJobs+4 tools), add
  job_id_human fix + ListJobs filter fix + .mcp.json env-var note; refresh
  priority list (bug-1490/1494/Phase2 all done).
- Scheduler-Agent-设计文档.md: v1.3, status header reflects Phase 0-2 done +
  verified.
- cerebrum: user preference (apiserver owns data correctness, MCP is a thin
  reader); jobid dual-encoding lesson (reuse backend.FormatJobID).
- memory: session log.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-13 03:35:00 +00:00
lei.chen
f096be1db5 docs: mark Phase 1 chatbot E2E verified (2026-08-12)
Full Agent chain verified on t1: mcp_operator API key → mcp-server
(scheduler_submit/get_job) → apiserver REST → controller → scheduler →
worker, job submit→run→DONE; cancel(kill)→EXIT. Note the apiserver
controller-restart rejoin gap (DataStore stays stale until apiserver
restart).

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-12 08:05:54 +00:00
lei.chen
7720d15cf9 docs: mark bug-1494 fully fixed (worker rejoin slots update, bug-1511)
bug-1510 (dispatch-fail re-PEND, 6928f74) + bug-1511 (sched_update_workers
in-place update on re-push, 1a2bd3f) together close bug-1494. Update
left_scheduler_work.md and buglog.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-11 06:15:11 +00:00
lei.chen
ce619c5583 docs: mark bug-1494 partially fixed (dispatch-fail re-PEND, bug-1510)
Document that the scheduler dispatch-fail re-PEND fix (commit 6928f74)
resolves one root cause of the "all jobs PEND" symptom; the
controller-restart self-heal-reconnect aspect of bug-1494 remains open.
Log bug-1510 in buglog.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-11 05:29:23 +00:00
lei.chen
c90e63ec0f fix(frontend): job control no longer returns HTTP 500 (bug-1490)
Two stacked defects in the apiserver job-control link, both of which kept
hold/release/cancel from ever reaching the controller:

1. controllers/jobs.go: validateJobControl and foundJob asserted
   jobinfo.Data["array_idx"].(int64), but StoreToOrderMaps produces
   array_idx as int (models.JobInfo.ArrayIdx is declared int) -> panic
   -> Gin Recovery -> HTTP 500 before backend.ControlJob ran. Add
   normalizeJobInt64 / jobArrayIdx helpers and use them in both
   functions (job_status already had this fix via normalizeJobStatus;
   array_idx was missed).

2. backend/clib.go: backend.ControlJob set xce_jobctrl_t.action to the
   models.JOB_* constant (JOB_STOP=1 / JOB_RESUME=2 / JOB_KILL=3 /
   JOB_CHKPNT=8), but that field is the capnp JobControl union
   discriminant (JobControl_u_siginfo=0 / JobControl_u_ckptoption=1).
   The controller's job_ctrl() selected the wrong union branch (or
   none, since encode_JobControl drops the union pointer for
   non-0/1 discriminants) and rejected every control with -EINVAL.
   Add jobCtrlDiscriminant() and use it.

Also: resolveExecUser was defined in the cgo-only convert.go but is
exercised by a non-cgo test, so `make test` (CGO_ENABLED=0) failed to
build the backend package. Move it to convert_pure.go so it compiles in
both modes.

Tests: 3 controller regression tests (validateJobControl allowed /
not-allowed branches, foundJob array-child branch) + 1 backend
jobCtrlDiscriminant test. make test fully green. Cluster E2E verified:
hold -> PSUSP, release -> PEND, cancel -> EXIT on a PEND job, no 500,
no -EINVAL, all daemons healthy.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-07 07:35:51 +00:00