merge: sync Rongsong's scheduler-framework (88 commits, f01ae353..c5b172c7)
Two-sided divergence from f01ae353 (2026-08-30): theirs +88 (worker
rejoin-resync reconciliation, terminal-state bug-1737/1738/1739/1764,
scheduler-sim verbosity, hermetic offline build, go mod vendor of Go deps
in apiserver -- 3375 pure-adds), ours +89 (job-finish robustness bug-1509
B1/B3, PEND-reason, worker reconnect resend, apiserver job control).
Conflict resolutions (kept both sides' fixes):
- controller_connector.c checking_connection(): combine theirs
xcecd_resync_is_pending() rejoin deferral gate with ours
resend_job_status() actual-state re-report on rejoin success.
- job.c: keep both unknown-finish registry (ours, bug-1509) and
record_job_finish_fact_at (theirs, bug-1747); keep both
mark_jobs_unknown_core family (ours) and record_job_reconcile_*
(theirs). No double-notify: on_job_finish unchanged, theirs is
resync-path-only.
- scheduler handler.c/test_handlers.c/meson: theirs schedExitJob
exit_code branch (bug-1737) + ours jobDispatchFail success-ack no-op
(bug-1512) + configpush enq_pend_update (bug-1513) both present; both
test suites and REPL/sim targets wired.
- apiserver jobs.go: keep ours normalizeJobInt64/jobArrayIdx superset +
JobIDHuman; drop theirs strict IntOrInt64 early-reject (superseded --
ours tolerates float64/json.Number, no panic path).
- jobs_test.go: keep both regression suites (ours bug-1490, theirs
bug-1742).
- .kiro/specs scheduler-sim-*/worker-rejoin-resync + .wolf/*: keep our
deletions (established convention -- drop Rongsong bookkeeping on merge).
Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
commit
5e2985542f
3469 changed files with 1466599 additions and 382 deletions
|
|
@ -378,7 +378,30 @@ static int sched_job_start_fun(int type, int situation, void* data, va_list ap)
|
|||
return rc;
|
||||
}
|
||||
|
||||
static int sched_job_exit_fun(int type, int situation, void* data, va_list ap)
|
||||
/*
|
||||
* sched_job_finish_fun (bug-1737, renamed from sched_job_exit_fun) --
|
||||
* subscribes to HOOK_DETAIL_JOB_EXIT, which despite its name fires for
|
||||
* EVERY job finish, both success and failure (record_job_finish_fact(),
|
||||
* job.c, decides DONE vs EXIT from finish->exit_code BEFORE firing this
|
||||
* same hook detail either way -- mirrors persist.c's own WAL_JOB_FINISH ->
|
||||
* HOOK_DETAIL_JOB_EXIT mapping, `case HOOK_DETAIL_JOB_EXIT: return
|
||||
* WAL_JOB_FINISH;`). This function previously discarded info->exit_code and
|
||||
* sent only the bare jobid, forcing sched_handle_notify() (scheduler/src/
|
||||
* handler.c) to unconditionally map EVERY finish -- including a clean,
|
||||
* successful one -- to JOB_STAT_EXIT. Under normal conditions a subsequent
|
||||
* HOOK_DETAIL_JOB_UPDATE fire (persist.c, same WAL apply) self-corrected
|
||||
* this moments later via schedUpdateJob's full xce_job_t (whose ->state is
|
||||
* already the real terminal value), but if THAT second notify was dropped
|
||||
* (c->sched.ep briefly NULL during a reconnect -- the same window bug-1731/
|
||||
* bug-1734 already documented) the scheduler was left permanently stuck
|
||||
* showing EXIT for a job the controller correctly recorded as DONE. Fix:
|
||||
* carry info (jobid + exit_code) across the wire via the existing
|
||||
* JobFinishInfo wire shape (same struct apiserver_job_exit_fun,
|
||||
* apiserver.c, already reuses for this exact hook) so
|
||||
* sched_handle_notify() can make the identical exit_code==0-vs-not decision
|
||||
* job.c itself makes, instead of guessing EXIT unconditionally.
|
||||
*/
|
||||
static int sched_job_finish_fun(int type, int situation, void* data, va_list ap)
|
||||
{
|
||||
cluster_t* c = va_arg(ap, cluster_t*);
|
||||
xce_jobfinish_t* info = va_arg(ap, xce_jobfinish_t*);
|
||||
|
|
@ -387,7 +410,7 @@ static int sched_job_exit_fun(int type, int situation, void* data, va_list ap)
|
|||
xce_notify_data_t xdata = {
|
||||
.event = NotifyData_data_schedExitJob,
|
||||
.data = {
|
||||
.sched_exit_job = info->jobid
|
||||
.sched_exit_job = info
|
||||
}
|
||||
};
|
||||
/* clang-format on */
|
||||
|
|
@ -408,6 +431,22 @@ static int sched_job_exit_fun(int type, int situation, void* data, va_list ap)
|
|||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* sched_job_fail_fun -- subscribes to HOOK_DETAIL_JOB_FAIL, a narrower event
|
||||
* than sched_job_finish_fun's HOOK_DETAIL_JOB_EXIT above: it fires for a
|
||||
* PIPELINE-stage failure (dispatch/RPC error, or a post-exec script
|
||||
* failing), not for the job's own exit code. apply_job_fail_transition()
|
||||
* (job.c) only forces JOB_STAT_EXIT for the `default:` case (a genuinely
|
||||
* pre-exit failure with no exit info yet); for JOB_EXEC_STAGE_POST_EXEC/
|
||||
* Q_POST_EXEC it MERGES a JOB_STAT_PERR flag onto whatever DONE/EXIT the
|
||||
* job's own finish already decided, never re-deciding DONE vs EXIT. This
|
||||
* function still always sends the same bare-jobid "treat as EXIT" notify
|
||||
* (matching the `default:` case only) -- bug-1738's
|
||||
* job_state_regresses_terminal() guard in core_events.c's
|
||||
* XCE_CORE_EVENT_JOB_STATE_CHANGE is what stops that from clobbering an
|
||||
* already-DONE job for the POST_EXEC case, since the scheduler has no
|
||||
* JOB_STAT_PERR-equivalent concept of its own to merge instead.
|
||||
*/
|
||||
static int sched_job_fail_fun(int type, int situation, void* data, va_list ap)
|
||||
{
|
||||
cluster_t* c = va_arg(ap, cluster_t*);
|
||||
|
|
@ -526,7 +565,7 @@ static int sched_job_hook_fun(int type, int situation, int detail, void* data,
|
|||
case HOOK_DETAIL_JOB_START:
|
||||
return sched_job_start_fun(type, situation, data, ap);
|
||||
case HOOK_DETAIL_JOB_EXIT:
|
||||
return sched_job_exit_fun(type, situation, data, ap);
|
||||
return sched_job_finish_fun(type, situation, data, ap);
|
||||
case HOOK_DETAIL_JOB_UPDATE:
|
||||
return sched_job_update_fun(type, situation, data, ap);
|
||||
case HOOK_DETAIL_JOB_FAIL:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue