fix(controller): mark RUN jobs unknown on worker loss and restart (bug-1509 P2)
Phase 2 of the job-finish robustness design (Part B, B1+B3). B1 (worker liveness): a worker that stops reporting (heartbeat timeout, already detected by check_worker_timeout_) or disconnects now has its RUN jobs marked JOB_STAT_UNKWN|JOB_STAT_TRANSIENT (transient_since set, durable WAL_JOB_UPDATE) instead of lingering as RUN forever. Marking happens AFTER the worker lock is released (B5: avoids worker->jobmgr inversion with the scheduler's jobmgr->worker order). B3 (restart): persist_recover marks all recovered RUN jobs UNKWN+TRANSIENT in-memory after replay — a controller restart leaves worker connectivity unknown. In-memory only (no WAL append) because persist_recover may run on a future standby that must never append. Unit tests (red->green): worker-loss marks its RUN jobs unknown, other-host jobs untouched, restart marks all RUN jobs. test_replay_apply_seam updated: replay still drives facts to RUN, then B3 adds UNKWN+TRANSIENT.
This commit is contained in:
parent
a793015f74
commit
fc091348e8
7 changed files with 224 additions and 4 deletions
|
|
@ -2098,6 +2098,91 @@ static int mark_job_update(cluster_t* c, job_t* job, int state)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* bug-1509 Phase 2 (B1): a worker that stops reporting (liveness timeout) or
|
||||
* disconnects is marked UNAVAIL, but its RUN jobs must not linger as RUN
|
||||
* forever — mark them UNKWN+TRANSIENT (transient_since = now) so (a) the
|
||||
* state is visibly "unknown" and (b) job_should_retain's TRANSIENT TTL can GC
|
||||
* them if the worker never comes back. hostname == NULL marks ALL RUN jobs
|
||||
* (used by persist_recover's restart marking, B3).
|
||||
*
|
||||
* Lock discipline (B5): the caller must NOT hold any worker or jobmgr lock.
|
||||
* This function takes the jobmgr read lock and per-job write locks, which
|
||||
* would invert the scheduler's jobmgr→worker order if a worker lock were held
|
||||
* here. */
|
||||
/* Shared core of the bug-1509 unknown-marking. persist=1 writes a durable
|
||||
* WAL_JOB_UPDATE (worker-death path, B1); persist=0 mutates in-memory only
|
||||
* (restart path, B3 — persist_recover runs before the controller knows it
|
||||
* will be primary, and a standby must never append). */
|
||||
static void mark_jobs_unknown_core(cluster_t* c, const char* hostname,
|
||||
int persist)
|
||||
{
|
||||
iterator_t it;
|
||||
job_t* j;
|
||||
|
||||
if (c == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
lock(c->jobmgr, RTTI_LOCK_READ);
|
||||
if (iter_make(&it, c->jobmgr) < 0) {
|
||||
unlock(c->jobmgr);
|
||||
return;
|
||||
}
|
||||
for (;;) {
|
||||
int rc = iter_next(&it, (void**)&j);
|
||||
if (rc < 0 || rc == RTTI_ITER_NO_DATA) {
|
||||
break;
|
||||
}
|
||||
if (j == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (hostname != NULL) {
|
||||
int on_host = 0;
|
||||
if (j->job.allocs != NULL && j->job.allocs->allocs != NULL) {
|
||||
for (int i = 0; i < j->job.allocs->nhosts; i++) {
|
||||
xce_host_alloc_t* ha = j->job.allocs->allocs[i];
|
||||
if (ha != NULL && ha->host != NULL
|
||||
&& strcmp(ha->host, hostname) == 0) {
|
||||
on_host = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!on_host) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
lock(j, RTTI_LOCK_WRITE);
|
||||
if (JOB_IS_RUN(j->job.state)
|
||||
&& !job_state_is_terminal(j->job.state)) {
|
||||
j->job.transient_since = time(NULL);
|
||||
if (persist) {
|
||||
mark_job_update(c, j, j->job.state | JOB_STAT_UNKWN
|
||||
| JOB_STAT_TRANSIENT);
|
||||
} else {
|
||||
j->job.state |= JOB_STAT_UNKWN | JOB_STAT_TRANSIENT;
|
||||
}
|
||||
}
|
||||
unlock(j);
|
||||
}
|
||||
iter_destroy(&it);
|
||||
unlock(c->jobmgr);
|
||||
}
|
||||
|
||||
void mark_worker_jobs_unknown(cluster_t* c, const char* hostname)
|
||||
{
|
||||
mark_jobs_unknown_core(c, hostname, 1);
|
||||
}
|
||||
|
||||
/* bug-1509 B3: restart path — after replay, RUN jobs' state is uncertain
|
||||
* (worker connectivity unknown). In-memory only; idempotent. */
|
||||
void mark_all_run_jobs_unknown_in_memory(cluster_t* c)
|
||||
{
|
||||
mark_jobs_unknown_core(c, NULL, 0);
|
||||
}
|
||||
|
||||
/* Guarded state-transition core for a postdone fact, extracted from
|
||||
* on_job_postdone's old inline mutation (raft-apply-decoupling task 6.1
|
||||
* round 2, Req 3.3/7.1-7.3). Unlike a generic UPDATE, postdone's formula
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue