feat(libxce-alloc-preason-equality): recorded dispatch fixtures carry the session-start from (task 4.2)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
6110d12e5a
commit
69112c2d22
7 changed files with 137 additions and 60 deletions
|
|
@ -53,7 +53,7 @@ Test-first throughout (red → green → refactor). Every sub-task leaves the bu
|
||||||
- Done when the three tests pass unchanged in outcome under the stricter checks; a failure is investigated as a real divergence, not loosened.
|
- Done when the three tests pass unchanged in outcome under the stricter checks; a failure is investigated as a real divergence, not loosened.
|
||||||
- _Requirements: 6.1, 6.2, 6.3, 6.7_
|
- _Requirements: 6.1, 6.2, 6.3, 6.7_
|
||||||
- _Depends: 2.1, 2.2, 3.1, 3.2_ (3.1 as well as 3.2: a normal-mode recording's begin time and its allocations' start time must come from one clock reading, or a recorded-vs-replayed comparison can differ by one second)
|
- _Depends: 2.1, 2.2, 3.1, 3.2_ (3.1 as well as 3.2: a normal-mode recording's begin time and its allocations' start time must come from one clock reading, or a recorded-vs-replayed comparison can differ by one second)
|
||||||
- [ ] 4.2 Bring hand-built recorded dispatch fixtures in line with the session start time
|
- [x] 4.2 Bring hand-built recorded dispatch fixtures in line with the session start time
|
||||||
- In every test that hand-builds a recorded dispatch later compared against a live replay, set the recorded allocation's reservation start time to the fixture session's recorded start in whole seconds (design.md, File Structure Plan: the six listed test files).
|
- In every test that hand-builds a recorded dispatch later compared against a live replay, set the recorded allocation's reservation start time to the fixture session's recorded start in whole seconds (design.md, File Structure Plan: the six listed test files).
|
||||||
- Update the comments in those files that describe the reservation start time as excluded or name the private comparators.
|
- Update the comments in those files that describe the reservation start time as excluded or name the private comparators.
|
||||||
- Harmless before 4.3, because the simulator still ignores the field; required after it.
|
- Harmless before 4.3, because the simulator still ignores the field; required after it.
|
||||||
|
|
@ -89,3 +89,4 @@ Test-first throughout (red → green → refactor). Every sub-task leaves the bu
|
||||||
- `xce-build.sh` resolves a relative build dir under `src/`: `bash src/tools/xce-build.sh lib asan xce.build.alloceq` builds into `src/xce.build.alloceq`, so iterate with `meson test -C src/xce.build.alloceq`.
|
- `xce-build.sh` resolves a relative build dir under `src/`: `bash src/tools/xce-build.sh lib asan xce.build.alloceq` builds into `src/xce.build.alloceq`, so iterate with `meson test -C src/xce.build.alloceq`.
|
||||||
- `xce-build.sh lib` runs only the `libxce test` binary; run `meson test -C src/xce.build.alloceq "libxce alloc OOM test"` (or plain `meson test`) for the allocation OOM binary, which also wraps `vasprintf`/`__vasprintf_chk` since 2.1.
|
- `xce-build.sh lib` runs only the `libxce test` binary; run `meson test -C src/xce.build.alloceq "libxce alloc OOM test"` (or plain `meson test`) for the allocation OOM binary, which also wraps `vasprintf`/`__vasprintf_chk` since 2.1.
|
||||||
- Simulation mode (`sim_loop.c`) still takes its own session-begin clock reading before `xce_core_begin_session()`; by design that is left to spec scheduler-sim-discrete-clock (3.1 fixed only the normal-mode recorder).
|
- Simulation mode (`sim_loop.c`) still takes its own session-begin clock reading before `xce_core_begin_session()`; by design that is left to spec scheduler-sim-discrete-clock (3.1 fixed only the normal-mode recorder).
|
||||||
|
- 4.2 left the `from`-excluded comments at `test_sim_verify.c` ~639-681 for 4.3, which inverts the test they document.
|
||||||
|
|
|
||||||
|
|
@ -842,13 +842,26 @@ static float lookup_want(uint64_t jobid)
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* SESSION_FROM - the `from` an allocation decided in a session that began at
|
||||||
|
* `begin_ns` (epoch nanoseconds) carries: the session start in whole
|
||||||
|
* seconds, floor division (libxce-alloc-preason-equality Decision 8;
|
||||||
|
* scheduler.c's sched_make_primary_alloc_() stamps it, and in simulation
|
||||||
|
* mode the session starts at the recorded beginRound). Hand-built recorded
|
||||||
|
* DISPATCH fixtures derive their `from` from the begin_round they are
|
||||||
|
* recorded under, so a live replay of the same round reproduces it. */
|
||||||
|
#define SESSION_FROM(begin_ns) ((uint64_t)(begin_ns) / 1000000000ULL)
|
||||||
|
|
||||||
|
/* The recorded round-1 beginRound the hand-built DISPATCH fixtures below
|
||||||
|
* are appended under (logical nanoseconds). */
|
||||||
|
#define ROUND1_BEGIN_NS 2000ULL
|
||||||
|
|
||||||
/* Builds the wire-shaped xce_job_alloc_t a DISPATCH decision carries --
|
/* Builds the wire-shaped xce_job_alloc_t a DISPATCH decision carries --
|
||||||
* shared by BOTH Part A's real dispatch construction below AND Part B's
|
* shared by BOTH Part A's real dispatch construction below AND Part B's
|
||||||
* hand-crafted decisions further down -- same field shape
|
* hand-crafted decisions further down -- same field shape
|
||||||
* test_sim_verify.c's own make_test_alloc()/make_test_host_alloc()/
|
* test_sim_verify.c's own make_test_alloc()/make_test_host_alloc()/
|
||||||
* make_test_job_alloc() establish. */
|
* make_test_job_alloc() establish. */
|
||||||
static xce_job_alloc_t* make_dispatch_alloc(uint64_t jobid, const char* host,
|
static xce_job_alloc_t* make_dispatch_alloc(uint64_t jobid, const char* host,
|
||||||
float value)
|
float value, uint64_t from)
|
||||||
{
|
{
|
||||||
xce_alloc_t* res = (xce_alloc_t*)calloc(1, sizeof(*res));
|
xce_alloc_t* res = (xce_alloc_t*)calloc(1, sizeof(*res));
|
||||||
xce_host_alloc_t* h = (xce_host_alloc_t*)calloc(1, sizeof(*h));
|
xce_host_alloc_t* h = (xce_host_alloc_t*)calloc(1, sizeof(*h));
|
||||||
|
|
@ -866,7 +879,7 @@ static xce_job_alloc_t* make_dispatch_alloc(uint64_t jobid, const char* host,
|
||||||
res->reserve = strdup("run");
|
res->reserve = strdup("run");
|
||||||
res->instid = 1;
|
res->instid = 1;
|
||||||
res->value = value;
|
res->value = value;
|
||||||
res->from = 0;
|
res->from = from;
|
||||||
res->duration = 0;
|
res->duration = 0;
|
||||||
|
|
||||||
allocs[0] = res;
|
allocs[0] = res;
|
||||||
|
|
@ -996,7 +1009,12 @@ static void* capacity_aware_allocator_thread_fn(void* arg)
|
||||||
g_slots_residx_normal, want);
|
g_slots_residx_normal, want);
|
||||||
sched_commit_job_alloc(core, job);
|
sched_commit_job_alloc(core, job);
|
||||||
|
|
||||||
job->allocs = make_dispatch_alloc(jobid, WORKER_NAME, want);
|
/* Stamp `from` exactly as sched_make_primary_alloc_()
|
||||||
|
* (scheduler.c) stamps a real allocation: the deciding
|
||||||
|
* session's start in whole seconds. */
|
||||||
|
job->allocs = make_dispatch_alloc(
|
||||||
|
jobid, WORKER_NAME, want,
|
||||||
|
SESSION_FROM(core->session_start));
|
||||||
|
|
||||||
/* This file's own addition -- see this function's header
|
/* This file's own addition -- see this function's header
|
||||||
* comment for why this is required. */
|
* comment for why this is required. */
|
||||||
|
|
@ -1416,13 +1434,15 @@ test_contention_parity_matches_real_normal_mode_recording_round_by_round(
|
||||||
append_job_state_change_event(f, 1800ULL, JOBC_ID, JOB_STAT_PEND,
|
append_job_state_change_event(f, 1800ULL, JOBC_ID, JOB_STAT_PEND,
|
||||||
JOB_STAT_RUN);
|
JOB_STAT_RUN);
|
||||||
|
|
||||||
allocA = make_dispatch_alloc(JOBA_ID, WORKER_NAME, JOBA_WANT);
|
allocA = make_dispatch_alloc(JOBA_ID, WORKER_NAME, JOBA_WANT,
|
||||||
allocC = make_dispatch_alloc(JOBC_ID, WORKER_NAME, JOBC_WANT);
|
SESSION_FROM(ROUND1_BEGIN_NS));
|
||||||
|
allocC = make_dispatch_alloc(JOBC_ID, WORKER_NAME, JOBC_WANT,
|
||||||
|
SESSION_FROM(ROUND1_BEGIN_NS));
|
||||||
|
|
||||||
round1_decisions[0] = make_decision_dispatch(2000ULL, allocA);
|
round1_decisions[0] = make_decision_dispatch(ROUND1_BEGIN_NS, allocA);
|
||||||
round1_decisions[1] = make_decision_dispatch(2000ULL, allocC);
|
round1_decisions[1] = make_decision_dispatch(ROUND1_BEGIN_NS, allocC);
|
||||||
round1_decisions[2] = make_decision_pend(2000ULL, JOBB_ID, 0);
|
round1_decisions[2] = make_decision_pend(ROUND1_BEGIN_NS, JOBB_ID, 0);
|
||||||
append_replay_session(f, 2000ULL, round1_decisions, 3);
|
append_replay_session(f, ROUND1_BEGIN_NS, round1_decisions, 3);
|
||||||
|
|
||||||
/* Round 2: job D pushed fresh -- no reconciliation involved (D was
|
/* Round 2: job D pushed fresh -- no reconciliation involved (D was
|
||||||
* never dispatched anywhere); sim's own live picker independently
|
* never dispatched anywhere); sim's own live picker independently
|
||||||
|
|
@ -1707,12 +1727,14 @@ static void write_multiround_contention_rounds(FILE* f, int upto_round)
|
||||||
append_jobs_push_event(f, 1500ULL, JOBB_ID, "normal");
|
append_jobs_push_event(f, 1500ULL, JOBB_ID, "normal");
|
||||||
append_jobs_push_event(f, 1500ULL, JOBC_ID, "normal");
|
append_jobs_push_event(f, 1500ULL, JOBC_ID, "normal");
|
||||||
|
|
||||||
allocA = make_dispatch_alloc(JOBA_ID, WORKER_NAME, JOBA_WANT);
|
allocA = make_dispatch_alloc(JOBA_ID, WORKER_NAME, JOBA_WANT,
|
||||||
allocC = make_dispatch_alloc(JOBC_ID, WORKER_NAME, JOBC_WANT);
|
SESSION_FROM(ROUND1_BEGIN_NS));
|
||||||
round1_decisions[0] = make_decision_dispatch(2000ULL, allocA);
|
allocC = make_dispatch_alloc(JOBC_ID, WORKER_NAME, JOBC_WANT,
|
||||||
round1_decisions[1] = make_decision_dispatch(2000ULL, allocC);
|
SESSION_FROM(ROUND1_BEGIN_NS));
|
||||||
round1_decisions[2] = make_decision_pend(2000ULL, JOBB_ID, 0);
|
round1_decisions[0] = make_decision_dispatch(ROUND1_BEGIN_NS, allocA);
|
||||||
append_replay_session(f, 2000ULL, round1_decisions, 3);
|
round1_decisions[1] = make_decision_dispatch(ROUND1_BEGIN_NS, allocC);
|
||||||
|
round1_decisions[2] = make_decision_pend(ROUND1_BEGIN_NS, JOBB_ID, 0);
|
||||||
|
append_replay_session(f, ROUND1_BEGIN_NS, round1_decisions, 3);
|
||||||
|
|
||||||
if (upto_round < 2) {
|
if (upto_round < 2) {
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -699,36 +699,32 @@ test_sim_verify_no_false_mismatch_after_live_dispatch_and_later_confirmation(
|
||||||
* 1-task job's TEST_RESREQ ("rusage[mem=1]", auto-injected 1 slot per
|
* 1-task job's TEST_RESREQ ("rusage[mem=1]", auto-injected 1 slot per
|
||||||
* task) actually requests.
|
* task) actually requests.
|
||||||
*
|
*
|
||||||
* sim_verify_alloc_eq() (sim_verify.c) compares xce_alloc_t
|
* job_alloc_eq() (xcealloc.h, via sim_verify.c's sim_decision_eq())
|
||||||
* field-by-field, including instid and duration -- both must match
|
* compares xce_alloc_t field-by-field, excluding only instid -- so
|
||||||
* EXACTLY what the real seqjob_allocator()->sched_make_allocs()-
|
* duration and from must match EXACTLY what the real
|
||||||
* >make_alloc() chain (scheduler.c) will independently produce for
|
* seqjob_allocator()->sched_make_allocs()->make_alloc() chain plus
|
||||||
* this exact fixture, not arbitrary placeholders:
|
* sched_make_primary_alloc_()'s session stamp (scheduler.c) will
|
||||||
* - instid=1: sched_get_id() returns core->nextid++, and core.c
|
* independently produce for this exact fixture, not placeholders:
|
||||||
* initializes nextid to 1 (core.c:127) -- this fixture's job is
|
|
||||||
* the FIRST (and only) allocation ever made against this fresh
|
|
||||||
* core, so it is deterministically the first ID issued.
|
|
||||||
* - duration=-1: seqjob_allocator.c's own sched_make_allocs() call
|
* - duration=-1: seqjob_allocator.c's own sched_make_allocs() call
|
||||||
* passes a NULL duration array ("FIXME: no duration now"), so
|
* passes a NULL duration array ("FIXME: no duration now"), so
|
||||||
* sched_make_allocs() (scheduler.c) always defaults every entry's
|
* sched_make_allocs() (scheduler.c) always defaults every entry's
|
||||||
* duration to -1 today.
|
* duration to -1 today.
|
||||||
* `from` (wall-clock time(NULL), set unconditionally by make_alloc()
|
* - from=ROUND1_BEGIN_NS / 10^9: every allocation is stamped with
|
||||||
* itself) is left at whatever value make_alloc() happens to stamp it
|
* its deciding session's start in whole seconds (libxce-alloc-
|
||||||
* with here -- it no longer needs to agree with the live path's own
|
* preason-equality Decision 8), and in simulation mode the session
|
||||||
* call. bug-1838-followup removed `from` from sim_verify_alloc_eq()'s
|
* starts at the recorded beginRound. make_alloc()'s own time(NULL)
|
||||||
* comparison entirely (sim_verify.c): it is a real-wall-clock
|
* default is therefore overwritten below.
|
||||||
* reservation-start stamp, not part of a decision's semantic identity,
|
* instid=1 is kept realistic (sched_get_id() returns core->nextid++,
|
||||||
* and comparing it made --sim-verify false-flag every genuine
|
* and this is the first allocation made against a fresh core) even
|
||||||
* live-dispatched decision replayed more than a wall-clock second away
|
* though the comparison ignores it. */
|
||||||
* from its own recording. What used to be accepted same-second-window
|
|
||||||
* flakiness here is now a non-issue by construction. */
|
|
||||||
{
|
{
|
||||||
|
const uint64_t ROUND1_BEGIN_NS = 2000ULL;
|
||||||
xce_alloc_t* a = make_alloc((char*)"slots", NULL, 1, 1.0,
|
xce_alloc_t* a = make_alloc((char*)"slots", NULL, 1, 1.0,
|
||||||
-1);
|
-1);
|
||||||
xce_alloc_t** allocs = make_allocs(1, a);
|
xce_alloc_t** allocs;
|
||||||
xce_host_alloc_t* h = make_host_alloc((char*)WORKER_NAME, 1, allocs);
|
xce_host_alloc_t* h;
|
||||||
xce_host_alloc_t** hallocs = make_host_allocs(1, h);
|
xce_host_alloc_t** hallocs;
|
||||||
xce_job_alloc_t* alloc = make_job_alloc(TEST_JOBID, 1, hallocs);
|
xce_job_alloc_t* alloc;
|
||||||
sched_dispatch_decision_t dispatch;
|
sched_dispatch_decision_t dispatch;
|
||||||
replay_decision_t d_dispatch;
|
replay_decision_t d_dispatch;
|
||||||
replay_decision_t* decisions[1];
|
replay_decision_t* decisions[1];
|
||||||
|
|
@ -737,18 +733,24 @@ test_sim_verify_no_false_mismatch_after_live_dispatch_and_later_confirmation(
|
||||||
struct capn_segment* cs;
|
struct capn_segment* cs;
|
||||||
ReplaySession_ptr sp;
|
ReplaySession_ptr sp;
|
||||||
|
|
||||||
|
a->from = ROUND1_BEGIN_NS / 1000000000ULL;
|
||||||
|
allocs = make_allocs(1, a);
|
||||||
|
h = make_host_alloc((char*)WORKER_NAME, 1, allocs);
|
||||||
|
hallocs = make_host_allocs(1, h);
|
||||||
|
alloc = make_job_alloc(TEST_JOBID, 1, hallocs);
|
||||||
|
|
||||||
memset(&dispatch, 0, sizeof(dispatch));
|
memset(&dispatch, 0, sizeof(dispatch));
|
||||||
dispatch.alloc = alloc;
|
dispatch.alloc = alloc;
|
||||||
|
|
||||||
memset(&d_dispatch, 0, sizeof(d_dispatch));
|
memset(&d_dispatch, 0, sizeof(d_dispatch));
|
||||||
d_dispatch.body_which = ReplayDecision_body_dispatch;
|
d_dispatch.body_which = ReplayDecision_body_dispatch;
|
||||||
d_dispatch.body.dispatch = &dispatch;
|
d_dispatch.body.dispatch = &dispatch;
|
||||||
d_dispatch.ts = 2000ULL;
|
d_dispatch.ts = ROUND1_BEGIN_NS;
|
||||||
|
|
||||||
decisions[0] = &d_dispatch;
|
decisions[0] = &d_dispatch;
|
||||||
|
|
||||||
memset(&session, 0, sizeof(session));
|
memset(&session, 0, sizeof(session));
|
||||||
session.begin_round = 2000ULL;
|
session.begin_round = ROUND1_BEGIN_NS;
|
||||||
session.decisions = decisions;
|
session.decisions = decisions;
|
||||||
session.n_decisions = 1;
|
session.n_decisions = 1;
|
||||||
session.summary = NULL;
|
session.summary = NULL;
|
||||||
|
|
|
||||||
|
|
@ -547,11 +547,16 @@ static replay_session_t* make_decoded_session(uint64_t begin_round,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* task 2.3 fixture helper: a real, one-host/one-resource xce_job_alloc_t --
|
/* task 2.3 fixture helper: a real, one-host/one-resource xce_job_alloc_t --
|
||||||
* same field shape sim_verify_job_alloc_eq() already validates (design.md's
|
* same field shape job_alloc_eq() (xcealloc.h) already validates (design.md's
|
||||||
* Allowed Dependencies), built via the real xcealloc.c production helpers
|
* Allowed Dependencies), built via the real xcealloc.c production helpers
|
||||||
* (make_alloc/make_allocs/make_host_alloc/make_host_allocs/make_job_alloc),
|
* (make_alloc/make_allocs/make_host_alloc/make_host_allocs/make_job_alloc),
|
||||||
* not a hand-rolled struct literal -- so a reconciled job's `job->accounts`
|
* not a hand-rolled struct literal -- so a reconciled job's `job->accounts`
|
||||||
* shape can be checked against real, non-trivial resource values. */
|
* shape can be checked against real, non-trivial resource values.
|
||||||
|
* `from` stays at make_alloc()'s time(NULL) default: these recorded
|
||||||
|
* DISPATCH entries only feed sim_reconcile_unaccounted_jobs() and are never
|
||||||
|
* compared against a live replay's allocation (whose `from` would be the
|
||||||
|
* session start in whole seconds, libxce-alloc-preason-equality
|
||||||
|
* Decision 8). */
|
||||||
static xce_job_alloc_t* make_test_job_alloc_with_host(uint64_t jobid,
|
static xce_job_alloc_t* make_test_job_alloc_with_host(uint64_t jobid,
|
||||||
const char* host,
|
const char* host,
|
||||||
const char* resname,
|
const char* resname,
|
||||||
|
|
|
||||||
|
|
@ -648,11 +648,21 @@ static float lookup_want(uint64_t jobid)
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* SESSION_FROM - the `from` an allocation decided in a session that began at
|
||||||
|
* `begin_ns` (epoch nanoseconds) carries: the session start in whole
|
||||||
|
* seconds, floor division (libxce-alloc-preason-equality Decision 8;
|
||||||
|
* scheduler.c's sched_make_primary_alloc_() stamps it, and in simulation
|
||||||
|
* mode the session starts at the recorded beginRound). This file's
|
||||||
|
* capacity-aware stand-in allocator stamps its DISPATCH allocations the
|
||||||
|
* same way, so the recording it produces carries the value a live replay
|
||||||
|
* of that round reproduces. */
|
||||||
|
#define SESSION_FROM(begin_ns) ((uint64_t)(begin_ns) / 1000000000ULL)
|
||||||
|
|
||||||
/* Builds the wire-shaped xce_job_alloc_t a DISPATCH decision carries -- same
|
/* Builds the wire-shaped xce_job_alloc_t a DISPATCH decision carries -- same
|
||||||
* field shape test_sim_verify.c's own make_test_alloc()/make_test_host_alloc()/
|
* field shape test_sim_verify.c's own make_test_alloc()/make_test_host_alloc()/
|
||||||
* make_test_job_alloc() establish. */
|
* make_test_job_alloc() establish. */
|
||||||
static xce_job_alloc_t* make_dispatch_alloc(uint64_t jobid, const char* host,
|
static xce_job_alloc_t* make_dispatch_alloc(uint64_t jobid, const char* host,
|
||||||
float value)
|
float value, uint64_t from)
|
||||||
{
|
{
|
||||||
xce_alloc_t* res = (xce_alloc_t*)calloc(1, sizeof(*res));
|
xce_alloc_t* res = (xce_alloc_t*)calloc(1, sizeof(*res));
|
||||||
xce_host_alloc_t* h = (xce_host_alloc_t*)calloc(1, sizeof(*h));
|
xce_host_alloc_t* h = (xce_host_alloc_t*)calloc(1, sizeof(*h));
|
||||||
|
|
@ -670,7 +680,7 @@ static xce_job_alloc_t* make_dispatch_alloc(uint64_t jobid, const char* host,
|
||||||
res->reserve = strdup("run");
|
res->reserve = strdup("run");
|
||||||
res->instid = 1;
|
res->instid = 1;
|
||||||
res->value = value;
|
res->value = value;
|
||||||
res->from = 0;
|
res->from = from;
|
||||||
res->duration = 0;
|
res->duration = 0;
|
||||||
|
|
||||||
allocs[0] = res;
|
allocs[0] = res;
|
||||||
|
|
@ -798,7 +808,12 @@ static void* capacity_aware_allocator_thread_fn(void* arg)
|
||||||
job->accounts = make_charge_account(WORKER_NAME, want);
|
job->accounts = make_charge_account(WORKER_NAME, want);
|
||||||
sched_commit_job_alloc(core, job);
|
sched_commit_job_alloc(core, job);
|
||||||
|
|
||||||
job->allocs = make_dispatch_alloc(jobid, WORKER_NAME, want);
|
/* Stamp `from` exactly as sched_make_primary_alloc_()
|
||||||
|
* (scheduler.c) stamps a real allocation: the deciding
|
||||||
|
* session's start in whole seconds. */
|
||||||
|
job->allocs = make_dispatch_alloc(
|
||||||
|
jobid, WORKER_NAME, want,
|
||||||
|
SESSION_FROM(core->session_start));
|
||||||
|
|
||||||
picker->picker_iface->on_dispatch(picker, jobid);
|
picker->picker_iface->on_dispatch(picker, jobid);
|
||||||
decision = make_dispatch_decision(job->allocs);
|
decision = make_dispatch_decision(job->allocs);
|
||||||
|
|
|
||||||
|
|
@ -675,11 +675,24 @@ static void append_end_of_stream_marker(FILE* f)
|
||||||
capn_free(&c);
|
capn_free(&c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* SESSION_FROM - the `from` an allocation decided in a session that began at
|
||||||
|
* `begin_ns` (epoch nanoseconds) carries: the session start in whole
|
||||||
|
* seconds, floor division (libxce-alloc-preason-equality Decision 8;
|
||||||
|
* scheduler.c's sched_make_primary_alloc_() stamps it, and in simulation
|
||||||
|
* mode the session starts at the recorded beginRound). Hand-built recorded
|
||||||
|
* DISPATCH fixtures derive their `from` from the begin_round they are
|
||||||
|
* recorded under, so a live replay of the same round reproduces it. */
|
||||||
|
#define SESSION_FROM(begin_ns) ((uint64_t)(begin_ns) / 1000000000ULL)
|
||||||
|
|
||||||
|
/* The recorded round-1 beginRound the hand-built DISPATCH fixtures below
|
||||||
|
* are appended under (logical nanoseconds). */
|
||||||
|
#define ROUND1_BEGIN_NS 2000ULL
|
||||||
|
|
||||||
/* Builds the wire-shaped xce_job_alloc_t a DISPATCH decision carries --
|
/* Builds the wire-shaped xce_job_alloc_t a DISPATCH decision carries --
|
||||||
* same field shape test_sim_verify.c's own make_test_alloc()/
|
* same field shape test_sim_verify.c's own make_test_alloc()/
|
||||||
* make_test_host_alloc()/make_test_job_alloc() establish. */
|
* make_test_host_alloc()/make_test_job_alloc() establish. */
|
||||||
static xce_job_alloc_t* make_dispatch_alloc(uint64_t jobid, const char* host,
|
static xce_job_alloc_t* make_dispatch_alloc(uint64_t jobid, const char* host,
|
||||||
float value)
|
float value, uint64_t from)
|
||||||
{
|
{
|
||||||
xce_alloc_t* res = (xce_alloc_t*)calloc(1, sizeof(*res));
|
xce_alloc_t* res = (xce_alloc_t*)calloc(1, sizeof(*res));
|
||||||
xce_host_alloc_t* h = (xce_host_alloc_t*)calloc(1, sizeof(*h));
|
xce_host_alloc_t* h = (xce_host_alloc_t*)calloc(1, sizeof(*h));
|
||||||
|
|
@ -697,7 +710,7 @@ static xce_job_alloc_t* make_dispatch_alloc(uint64_t jobid, const char* host,
|
||||||
res->reserve = strdup("run");
|
res->reserve = strdup("run");
|
||||||
res->instid = 1;
|
res->instid = 1;
|
||||||
res->value = value;
|
res->value = value;
|
||||||
res->from = 0;
|
res->from = from;
|
||||||
res->duration = 0;
|
res->duration = 0;
|
||||||
|
|
||||||
allocs[0] = res;
|
allocs[0] = res;
|
||||||
|
|
@ -843,9 +856,10 @@ test_reconciled_job_charges_resinuse_before_live_capacity_check(void)
|
||||||
append_job_state_change_event(f, 1800ULL, JOBA_ID, JOB_STAT_PEND,
|
append_job_state_change_event(f, 1800ULL, JOBA_ID, JOB_STAT_PEND,
|
||||||
JOB_STAT_RUN);
|
JOB_STAT_RUN);
|
||||||
|
|
||||||
alloc = make_dispatch_alloc(JOBA_ID, WORKER_NAME, JOBA_WANT);
|
alloc = make_dispatch_alloc(JOBA_ID, WORKER_NAME, JOBA_WANT,
|
||||||
append_replay_session_with_dispatch_and_pend(f, 2000ULL, alloc, JOBB_ID,
|
SESSION_FROM(ROUND1_BEGIN_NS));
|
||||||
0);
|
append_replay_session_with_dispatch_and_pend(f, ROUND1_BEGIN_NS, alloc,
|
||||||
|
JOBB_ID, 0);
|
||||||
append_end_of_stream_marker(f);
|
append_end_of_stream_marker(f);
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -460,6 +460,19 @@ void tearDown(void) { return; }
|
||||||
/* Unit-level fixture helpers: hand-built xce_job_alloc_t trees. */
|
/* Unit-level fixture helpers: hand-built xce_job_alloc_t trees. */
|
||||||
/* ---------------------------------------------------------------------- */
|
/* ---------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
/* SESSION_FROM - the `from` a DISPATCH allocation decided in a session that
|
||||||
|
* began at `begin_ns` (epoch nanoseconds) carries: the session start in
|
||||||
|
* whole seconds, floor division (libxce-alloc-preason-equality Decision 8;
|
||||||
|
* scheduler.c's sched_make_primary_alloc_() stamps it, and in simulation
|
||||||
|
* mode the session starts at the recorded beginRound). Hand-built recorded
|
||||||
|
* DISPATCH fixtures replayed through sched_sim_driver_run() below derive
|
||||||
|
* their `from` from the same begin_round they are recorded under. */
|
||||||
|
#define SESSION_FROM(begin_ns) ((uint64_t)(begin_ns) / 1000000000ULL)
|
||||||
|
|
||||||
|
/* The recorded round-1 beginRound those DISPATCH fixtures are appended
|
||||||
|
* under (logical nanoseconds). */
|
||||||
|
#define ROUND1_BEGIN_NS 2000ULL
|
||||||
|
|
||||||
static xce_alloc_t* make_test_alloc(const char* resname, const char* reserve,
|
static xce_alloc_t* make_test_alloc(const char* resname, const char* reserve,
|
||||||
uint64_t instid, double value,
|
uint64_t instid, double value,
|
||||||
uint64_t from, int64_t duration)
|
uint64_t from, int64_t duration)
|
||||||
|
|
@ -1645,12 +1658,13 @@ static void test_verify_with_already_resolved_job_reports_no_mismatch(void)
|
||||||
append_job_state_change_event(f, 1800ULL, 501ULL, JOB_STAT_PEND,
|
append_job_state_change_event(f, 1800ULL, 501ULL, JOB_STAT_PEND,
|
||||||
JOB_STAT_RUN);
|
JOB_STAT_RUN);
|
||||||
|
|
||||||
res[0] = make_test_alloc("slots", "run", 1, 4.0, 0, 0);
|
res[0] = make_test_alloc("slots", "run", 1, 4.0,
|
||||||
|
SESSION_FROM(ROUND1_BEGIN_NS), 0);
|
||||||
host[0] = make_test_host_alloc("hostA", res, 1);
|
host[0] = make_test_host_alloc("hostA", res, 1);
|
||||||
alloc = make_test_job_alloc(501ULL, host, 1);
|
alloc = make_test_job_alloc(501ULL, host, 1);
|
||||||
|
|
||||||
append_replay_session_with_dispatch_and_pend(f, 2000ULL, alloc, 502ULL,
|
append_replay_session_with_dispatch_and_pend(f, ROUND1_BEGIN_NS, alloc,
|
||||||
0);
|
502ULL, 0);
|
||||||
append_end_of_stream_marker(f);
|
append_end_of_stream_marker(f);
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
|
||||||
|
|
@ -1722,18 +1736,21 @@ test_verify_with_multiple_already_resolved_jobs_across_rounds_reports_no_mismatc
|
||||||
append_job_state_change_event(f, 1801ULL, 503ULL, JOB_STAT_PEND,
|
append_job_state_change_event(f, 1801ULL, 503ULL, JOB_STAT_PEND,
|
||||||
JOB_STAT_RUN);
|
JOB_STAT_RUN);
|
||||||
|
|
||||||
res_a[0] = make_test_alloc("slots", "run", 1, 4.0, 0, 0);
|
res_a[0] = make_test_alloc("slots", "run", 1, 4.0,
|
||||||
|
SESSION_FROM(ROUND1_BEGIN_NS), 0);
|
||||||
host_a[0] = make_test_host_alloc("hostA", res_a, 1);
|
host_a[0] = make_test_host_alloc("hostA", res_a, 1);
|
||||||
alloc_a = make_test_job_alloc(501ULL, host_a, 1);
|
alloc_a = make_test_job_alloc(501ULL, host_a, 1);
|
||||||
|
|
||||||
res_c[0] = make_test_alloc("slots", "run", 1, 2.0, 0, 0);
|
res_c[0] = make_test_alloc("slots", "run", 1, 2.0,
|
||||||
|
SESSION_FROM(ROUND1_BEGIN_NS), 0);
|
||||||
host_c[0] = make_test_host_alloc("hostC", res_c, 1);
|
host_c[0] = make_test_host_alloc("hostC", res_c, 1);
|
||||||
alloc_c = make_test_job_alloc(503ULL, host_c, 1);
|
alloc_c = make_test_job_alloc(503ULL, host_c, 1);
|
||||||
|
|
||||||
/* Recorded order: DISPATCH(A), PEND(B), DISPATCH(C) -- B's entry is
|
/* Recorded order: DISPATCH(A), PEND(B), DISPATCH(C) -- B's entry is
|
||||||
* sandwiched between the two entries reconciliation consumes. */
|
* sandwiched between the two entries reconciliation consumes. */
|
||||||
append_replay_session_with_two_dispatches_and_pend(f, 2000ULL, alloc_a,
|
append_replay_session_with_two_dispatches_and_pend(f, ROUND1_BEGIN_NS,
|
||||||
502ULL, 0, alloc_c);
|
alloc_a, 502ULL, 0,
|
||||||
|
alloc_c);
|
||||||
|
|
||||||
/* Round 2: job B (502) is still genuinely PEND (never dispatched, never
|
/* Round 2: job B (502) is still genuinely PEND (never dispatched, never
|
||||||
* state-changed) so builtin_fcfs's pendJobs discipline re-offers it to
|
* state-changed) so builtin_fcfs's pendJobs discipline re-offers it to
|
||||||
|
|
@ -1821,7 +1838,8 @@ test_reconciliation_no_longer_masks_a_pend_jobs_dispatch_mismatch(void)
|
||||||
append_queue_update_event(f, 1000ULL, "normal");
|
append_queue_update_event(f, 1000ULL, "normal");
|
||||||
append_jobs_push_event(f, 1500ULL, 601ULL, "normal");
|
append_jobs_push_event(f, 1500ULL, 601ULL, "normal");
|
||||||
|
|
||||||
res[0] = make_test_alloc("slots", "run", 1, 4.0, 0, 0);
|
res[0] = make_test_alloc("slots", "run", 1, 4.0,
|
||||||
|
SESSION_FROM(ROUND1_BEGIN_NS), 0);
|
||||||
host[0] = make_test_host_alloc("hostA", res, 1);
|
host[0] = make_test_host_alloc("hostA", res, 1);
|
||||||
alloc = make_test_job_alloc(601ULL, host, 1);
|
alloc = make_test_job_alloc(601ULL, host, 1);
|
||||||
|
|
||||||
|
|
@ -1832,7 +1850,7 @@ test_reconciliation_no_longer_masks_a_pend_jobs_dispatch_mismatch(void)
|
||||||
* header comment) is the only thing that CAN decide it, and it always
|
* header comment) is the only thing that CAN decide it, and it always
|
||||||
* PENDs. The live stream therefore produces PEND(601) against a
|
* PENDs. The live stream therefore produces PEND(601) against a
|
||||||
* recorded DISPATCH(601) -- a genuine, correctly-reported mismatch. */
|
* recorded DISPATCH(601) -- a genuine, correctly-reported mismatch. */
|
||||||
append_replay_session_with_dispatch(f, 2000ULL, alloc);
|
append_replay_session_with_dispatch(f, ROUND1_BEGIN_NS, alloc);
|
||||||
|
|
||||||
/* Round 2: job 601 is STILL a picker candidate (nothing retired it --
|
/* Round 2: job 601 is STILL a picker candidate (nothing retired it --
|
||||||
* reconciliation never touched it, and the stub allocator's on_pend()
|
* reconciliation never touched it, and the stub allocator's on_pend()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue