New scheduler-sim-eager-reconciliation requirements spec; weekly_report skill; weekly status report for Aug 16-22. Also records cross-spec dependency notes on scheduler-allocation and scheduler-preemption reflecting how scheduler-dispatch-commit's redefined RUN-state timing affects their designs. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AgYRU3zDTqNGV5Jk8aiKLg
9.8 KiB
Requirements Document
Introduction
The XCE scheduler currently has no job preemption mechanism. When a high-priority job cannot be scheduled because all resources are consumed by lower-priority running jobs, the only available action is to pend the job — it waits indefinitely until running jobs complete naturally, regardless of priority.
This feature closes the preemption gap with three additions:
- A
XCE_DECISION_PREEMPTdecision type flowing through the same callback asDISPATCHandPEND, carrying victim job ids and signal type to the controller→worker pipeline. - A configurable
preempt_policyplugin mechanism for victim selection, with a built-in"lowest_priority_first"default policy implementing LSF-style cross-queue victim selection. - A SIGSTP delivery pipeline: scheduler emits
XCE_DECISION_PREEMPT→ controller deliversHOOK_DETAIL_JOB_STOPto each victim's worker → worker sends SIGSTP to the job's process group → controller transitions the job toJOB_SUSPENDEDand releases resources.
The design uses async preemption: the preempting job is pended and retried in the next scheduling round after resources are freed, rather than blocking the scheduling loop waiting for victims to stop.
Full discovery context (problem, current state, alternatives considered, approach, boundary) is in
.kiro/specs/scheduler-preemption/brief.md.This spec is a child of
scheduler-framework. It depends on scheduler-core for the decision callback (xce_core_decision_cb_t) through whichXCE_DECISION_PREEMPTflows. The preemption branch (Level 3) fires in the core scheduling loop after allocation failure, before pending — this integration point is defined in scheduler-core's requirements.
Boundary Context
-
In scope:
XCE_DECISION_PREEMPTopcode andxce_preempt_decision_tpayload indecision.h;preempt_policyplugin interface for victim selection; default"lowest_priority_first"preemption policy implementing LSF-style cross-queue victim selection; per-queue preemption configuration (preemptible_queues,preemptible,preempt_policyfields onxce_sched_queue_t); SIGSTP delivery pipeline through controller→worker;JOB_SUSPENDEDstate transition on victim jobs; reuse ofHOOK_DETAIL_JOB_STOPfor preemption stop notifications; victim safety guards (never preempt same-or-higher priority, never preempt non-preemptiblequeues, never over-preempt); integration with the core scheduling loop at Level 3 (alloc-fail branch). -
Out of scope: Kill-and-requeue escalation beyond SIGSTP (deferred — v1 is SIGSTP only); deadline-based preemption (requires
deadlinefield onxce_sched_job_t, deferred); preempted job resume logic (theJOB_SUSPENDEDstate is defined but resume is a separate feature); fair-share or proportional victim selection policies beyond the default (the plugin interface enables them, but no additional policies ship here); changes to the picker interface, iterator abstractions, core/driver separation, or the scheduling loop structure itself (those belong to sibling specsscheduler-coreandscheduler-allocation). -
Adjacent expectations: This spec depends on
scheduler-corefor the decision callback (xce_core_decision_cb_t) and the core scheduling loop into which the preemption branch integrates. The existing controller→worker message pipeline for dispatch/pend is extended to carryXCE_DECISION_PREEMPT. The existingHOOK_DETAIL_JOB_STOPhook detail enum (value 65 incluster_def.h) is reused for preemption stop actions. The existingplugin_register/key_findmechanism loads thepreempt_policyplugin.
Data Structure Changes
decision.h additions
typedef enum {
XCE_DECISION_NONE = 0,
XCE_DECISION_DISPATCH,
XCE_DECISION_PEND,
XCE_DECISION_PREEMPT, // NEW — preemption decision
XCE_DECISION_NEW_SESSION
} xce_decision_op_t;
typedef struct {
uint64_t* victim_jobids; // jobs to preempt
int n_victims; // count of victims
int signal; // signal to send (SIGSTP in v1, SIGKILL in future)
uint64_t preempting_jobid; // job that triggered preemption
} xce_preempt_decision_t;
xce_sched_queue_t additions
char** preemptible_queues; // queue names this queue CAN preempt from (NULL = none)
int n_preemptible_queues; // count of preemptible_queues entries
bool preemptible; // CAN this queue's jobs BE preempted by other queues?
char* preempt_policy; // victim-selection plugin name (NULL = use default)
Preemption Flow
scheduler controller worker
─────────── ────────── ──────
alloc fails for high-prio job J
preempt_policy.select_victims(J):
collect running jobs from
Q_aggressor.preemptible_queues
filter: victim.prio < J.prio
filter: victim.queue.preemptible
rank: lowest priority first
select minimum set satisfying J.resreq
→ [victim1, victim2]
XCE_DECISION_PREEMPT ────────────▶ handle_job_preempt() ──────▶ SIGSTP victim1
.victim_jobids = [v1, v2] deliver to each job → JOB_SUSPENDED
.signal = SIGSTP victim's worker release resources
.preempting_jobid = J
J pended → next scheduling round → allocation retries on freed resources
Requirements
Requirement: Preemption policy and execution
Objective: As a cluster operator, I want high-priority jobs to trigger preemption of lower-priority running jobs when resources are insufficient, so that critical work can proceed without manual intervention.
Acceptance Criteria
-
The Scheduler shall add a
XCE_DECISION_PREEMPTopcode toxce_decision_op_tindecision.h, with a payload typexce_preempt_decision_tcarryingvictim_jobids[],n_victims,signal(SIGSTP in v1), andpreempting_jobid. TheXCE_DECISION_PREEMPTdecision shall flow through the samexce_core_decision_cb_tcallback asDISPATCHandPEND; the core scheduling loop shall contain zero knowledge of how the driver publishes preemption decisions. -
When allocation fails for a job J whose queue has a non-NULL
preemptible_queueslist, the Scheduler shall invoke the queue'spreempt_policyplugin to select victim jobs from the target queues. Whenpreempt_policyis NULL, the Scheduler shall use the default"lowest_priority_first"policy. -
The default preemption policy
"lowest_priority_first"shall: WHILE the preempting job's resource request is not yet satisfied DO collect running jobs from target queues; filter to those withpriority < J.priorityANDqueue->preemptible == true; rank by priority ascending (lowest priority first); select the next victim candidate and add to the victim set. The policy shall return the minimum subset of victims whose freed resources satisfy J's resource request. When multiple victim subsets of equal minimum size exist, the policy shall prefer the subset whose victims have the lowest summed priority. -
When a sufficient victim set exists, the Scheduler shall commit the preemption decision via the decision callback, and shall pend job J for the next scheduling round (async preemption — the job is retried after victim resources are freed). The Scheduler shall not wait synchronously for victim jobs to transition to
JOB_SUSPENDED. -
When no sufficient victim set exists after exhausting all eligible running jobs from the target queues, the Scheduler shall pend job J with a resource-shortage pend reason without invoking preemption. The decision callback shall not be called for the preemption path in this case.
-
The worker receiving a preemption action shall send the specified signal (SIGSTP in v1) to the victim job's process group. The controller shall transition the victim job's state to
JOB_SUSPENDEDafter confirming the signal was delivered. Resources held by the victim job shall be released upon suspension. -
The Scheduler shall add
preemptible_queues(list of queue names this queue CAN preempt from, NULL = none),preemptible(bool — CAN this queue's jobs BE preempted by other queues, default false), andpreempt_policy(victim-selection plugin name, NULL = use default"lowest_priority_first") fields toxce_sched_queue_t. These fields shall be populated from queue configuration atQUEUE_UPDATEtime. -
The existing
HOOK_DETAIL_JOB_STOPhook detail enum (value 65 incluster_def.h) shall be reused for preemption stop notifications, with no changes to the enum definition. The controller shall deliver this hook detail to each victim's worker as part of the preemption action. -
Preemption victim selection shall never select:
- A job whose priority is greater than or equal to the preempting job's priority.
- A job whose queue has
preemptible == false. - A job that is not in running state.
- A job from a queue not listed in the preempting job's
preemptible_queueslist. - More victims than necessary to satisfy the preempting job's resource request (minimum sufficient victim set).
Pending revision (2026-08-23): the sibling spec
scheduler-dispatch-commit(not yet designed) redefines "running state" to begin at dispatch-decision-commit time rather than worker-confirmation time — meaning a job could reach RUN with resources committed but no live, signalable process yet. AC6's SIGSTP-to-process-group delivery and AC9's "not in running state" exclusion both assume every RUN job has a live process today; oncescheduler-dispatch-commitlands, this spec's design phase needs to add a second victim action (cancel an in-flight, unconfirmed dispatch) alongside SIGSTP, and decide whether a dispatched-but-unconfirmed job is eligible as a victim at all. Re-check this note beforescheduler-preemptionproceeds to/kiro-spec-design.