feat(scheduler-core): implement snapshot save and load (task 8.1)
Adds xce_sched_core_save_snapshot()/xce_sched_core_load_snapshot(), split across snapshot.c/snapshot_wire.c/snapshot_load.c (800-line cap) along real save/load/shared-helper boundaries. Serializes the full core state (config, jobs, queues, workers, resources, resvals, users, projects, prng_seed, internal_state) into/out of sched_snapshot_data_t (task 7.3's committed C mirror of the capnp SnapshotData struct) as plain in-memory structures -- no wire encoding here, that's a later task. After load, rebuilds the picker's private state by re-invoking its state-change hooks (queue updates, then job pushes) against the restored core state, matching xce_core_push_events()'s own hook-firing order; picker-private state itself is never stored in the snapshot. Adds a new prng_seed field to xce_sched_core_t -- requirements.md/ design.md both presuppose it but no prior task added it. Seeded to 0, no PRNG consumer wired yet (deliberately out of scope). Cross-core behavioral-equivalence proven directly: two independent cores, each with its own registered picker, save on one and load into a freshly-initialized other, both driven through an identical real session, decision sequences asserted identical in order. Found and fixed two real bugs along the way: a missing prng_seed restore in this task's own new code, and a genuine guard bug in already-committed core.c (sched_get_user()/sched_get_project() treating key_find()'s expected -ENOENT as fatal, silently breaking get-or-create for any new name) that also affects the live production path (make_sched_job(), JOBS_PUSH) -- a narrowly-scoped two-line fix, independently traced to the primitive level and confirmed correct by review. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019dw5WnN3DXStaEaPYfACU1
This commit is contained in:
parent
7704710985
commit
1a32e63a86
10 changed files with 2383 additions and 4 deletions
|
|
@ -164,6 +164,30 @@ struct xce_sched_core_s {
|
|||
*/
|
||||
uint64_t session_start;
|
||||
uint64_t session_duration_ns;
|
||||
/*
|
||||
* PRNG seed for deterministic tie-breaking (Req 5 AC 7 / task 8.1). This
|
||||
* field did not exist before task 8.1 -- requirements.md's Req 5 AC 7
|
||||
* explicitly names `core->prng_seed` ("Any required randomness ... shall
|
||||
* use a PRNG seeded by the driver at init time") and design.md's
|
||||
* eventual `xce_sched_core_init(core, decision_cb, driver_ctx,
|
||||
* prng_seed)` signature (see this header's own doc comment above
|
||||
* xce_sched_core_init()) both presuppose it, but no prior task added it
|
||||
* or wired a PRNG consumer -- confirmed by grepping this entire spec's
|
||||
* source tree for "prng"/"seed" before this task. Adding ONLY the field
|
||||
* here, seeded to a deterministic constant (0) by
|
||||
* xce_sched_core_init(), keeps this task's scope to "the field exists
|
||||
* and round-trips through save_snapshot()/load_snapshot()"
|
||||
* (Req 5 AC 21's "PRNG state" checklist row) -- NOT changing
|
||||
* xce_sched_core_init()'s signature (still deliberately narrower per
|
||||
* this header's doc comment below) and NOT wiring any actual PRNG
|
||||
* consumer, since nothing in the codebase performs tie-breaking
|
||||
* randomness yet (builtin_fcfs's ordering is fully deterministic via
|
||||
* ordq comparators, no PRNG call site exists to seed). A future task
|
||||
* that adds real PRNG-based tie-breaking is expected to add its own
|
||||
* setter, the same pattern xce_core_set_decision_cb() already
|
||||
* establishes for driver_ctx/on_decision.
|
||||
*/
|
||||
uint64_t prng_seed;
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
@ -457,4 +481,70 @@ extern uint64_t xce_core_now(xce_sched_core_t*);
|
|||
extern void xce_core_set_active_for_logging(xce_sched_core_t*);
|
||||
extern int xce_core_register_tlog_format(void);
|
||||
|
||||
/*
|
||||
* SCHED_SNAPSHOT_INTERNAL_VERSION - the current format tag for
|
||||
* `sched_snapshot_data_t.internal_state`'s opaque byte layout (task 8.1).
|
||||
* Declared here, rather than privately in snapshot.c, because BOTH
|
||||
* xce_sched_core_save_snapshot() (snapshot.c, which writes it) and
|
||||
* xce_sched_core_load_snapshot() (snapshot_load.c, which version-checks
|
||||
* it) need the identical value -- a shared header is the single-source-of-
|
||||
* truth home for a constant two separate translation units must agree on
|
||||
* bit-for-bit; duplicating it as two independent #defines would risk
|
||||
* exactly the kind of silent drift this task's own documentation
|
||||
* (snapshot.c's file header, "internal_state contents") goes out of its
|
||||
* way to avoid elsewhere. This is an internal implementation detail of the
|
||||
* two functions below, not part of any wire/capnp contract -- callers of
|
||||
* either function never need to read it directly.
|
||||
*/
|
||||
#define SCHED_SNAPSHOT_INTERNAL_VERSION (1u)
|
||||
|
||||
/*
|
||||
* xce_sched_core_save_snapshot / xce_sched_core_load_snapshot - Req 5 AC 21
|
||||
* / Req 6 AC 5 (scheduler-core spec, task 8.1; design.md "SchedulerCore"
|
||||
* Service Interface). Serialize the full core state into (out of) a
|
||||
* `sched_snapshot_data_t` (typemap.h, the C mirror of task 7.3's capnp
|
||||
* `SnapshotData` struct) -- populate/consume it from `xce_sched_core_t`'s
|
||||
* live in-memory state directly; NO capnp encode/decode happens here (that
|
||||
* is a later, wire-format task's job). Implemented across three files --
|
||||
* snapshot.c (the save direction + the full shared design-decision
|
||||
* documentation), snapshot_wire.c (small helpers + save-direction wire
|
||||
* builders), and snapshot_load.c (the load direction + picker-rebuild) --
|
||||
* split for this project's 800-line-per-file convention; see snapshot.c's
|
||||
* own file header comment, "Three-file split", for the full rationale.
|
||||
*
|
||||
* Both are only valid between scheduling sessions, matching every other
|
||||
* core entry point's precondition (xce_core_push_events()'s own doc
|
||||
* comment, above): return -EBUSY, with ZERO state touched, if
|
||||
* `core->session_active != 0` -- checked BEFORE any mutation, so a rejected
|
||||
* call is a true no-op (same pattern xce_core_begin_session()/
|
||||
* xce_core_push_events() already establish). Both return -EINVAL for a
|
||||
* NULL core or a NULL out/in pointer.
|
||||
*
|
||||
* xce_sched_core_save_snapshot() populates `*out` from `*core`'s current
|
||||
* state (jobs/queues/workers/resources/resvals/users/projects/config/
|
||||
* prng_seed/internal_state); see snapshot.c's own file header comment for
|
||||
* the full field-by-field mapping and the documented scope decisions
|
||||
* (job-level priority and in-flight allocations are NOT captured -- see
|
||||
* that comment for why).
|
||||
*
|
||||
* xce_sched_core_load_snapshot() restores `*core` (which must be freshly
|
||||
* initialized via xce_sched_core_init() -- loading into a core that
|
||||
* already holds state is not this task's scope, and is not what the
|
||||
* driver-side callers design.md documents -- record start, periodic
|
||||
* checkpoint, simulation startup -- ever do) from `*in`, THEN rebuilds the
|
||||
* resolved picker's private state by walking the just-restored core state
|
||||
* and re-invoking the picker's state-change hooks in the same order
|
||||
* xce_core_push_events() already establishes for state-change hooks:
|
||||
* on_queue_update for every restored queue, THEN on_job_push for every
|
||||
* restored job. Picker-private state is deliberately never stored in the
|
||||
* snapshot itself (requirements.md Req 5 AC 21's own "Picker state is NOT
|
||||
* stored in snapshot" rule) -- this rebuild is what makes post-load state
|
||||
* indistinguishable from state built up via normal xce_core_push_events()
|
||||
* calls, which is the whole point of the Observable this task proves.
|
||||
*/
|
||||
extern int xce_sched_core_save_snapshot(xce_sched_core_t*,
|
||||
sched_snapshot_data_t*);
|
||||
extern int xce_sched_core_load_snapshot(xce_sched_core_t*,
|
||||
const sched_snapshot_data_t*);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue