feat(job-array-single-wal-record): API-server-side (Go) local expansion of the compact push

Adds the Go/CGO receive path for Data.jobArray, mirroring task 5.3's
scheduler-side C implementation: rawJobArrayToModels() (pure-Go
base_jobid + i expansion), cJobArrayToModels() (CGO adapter sibling to
cJobToModel), dispatchJobArray() (dedup via the same store.Jobs.Has()
the ordinary Data_data_jobs path already writes through, single
confirmation log only when at least one element was newly
dispatched), and a new case C.Data_data_jobArray in handleDataset.

Elements route through OnJobUpdated, matching the ordinary single-job
push's own routing. The CGO boundary needed no changes -- both
C.xce_job_submit_t and C.Data_data_jobArray were already exposed from
prior tasks, including task 5.3b's extract_data() fix which this
receive path depends on identically to the scheduler's.

store.ArrayJobs (the array-level rollup aggregate) is deliberately
left unpopulated -- confirmed out of this spec's scope, deferred to a
future spec per requirements.md's Boundary Context.

Task 5.4 of job-array-single-wal-record (Requirements 6.2, 6.4, 6.5, 8.4).
This commit is contained in:
Rongsong Shen 2026-07-15 15:56:48 +00:00
parent 1cbd7df1a8
commit 9e0d87fe50
7 changed files with 345 additions and 9 deletions

View file

@ -28,6 +28,48 @@ func goString(s *C.char) string {
return C.GoString(s)
}
// cJobArrayToModels decodes a compact Data.jobArray push descriptor
// (*C.xce_job_submit_t: base jobid, array_size, one shared job_submit_data_t
// spec — the same JobSubmit shape apiserver_push_jobarray() sends,
// job-array-single-wal-record task 5.2) and expands it locally into N
// models.JobInfo entries via rawJobArrayToModels, mirroring cJobToModel's
// single-job field extraction below but for the array-shaped descriptor
// (task 5.4, Req 6.2, 6.5). Decodes the shared submit spec exactly once
// (same field-by-field extraction cJobToModel uses for job.submit) since
// every element shares it by construction — never once per element.
func cJobArrayToModels(jobArray *C.xce_job_submit_t) []*models.JobInfo {
if jobArray == nil || jobArray.submit == nil {
return nil
}
s := jobArray.submit
rd := &rawSubmitData{
queue: goString(s.queue),
user: goString(s.user),
jobName: goString(s.job_name),
projectName: goString(s.project_name),
userGroup: goString(s.user_group),
jobDesc: goString(s.job_description),
resReq: goString(s.resreq),
minTasks: int(s.mintasks),
maxTasks: int(s.maxtasks),
}
if s.start_info != nil {
si := s.start_info
rd.startInfo = &rawStartInfo{
cmdline: goString(si.cmdline),
launchType: goString(si.launch_type),
preExec: goString(si.job_pre_exec),
postExec: goString(si.job_post_exec),
execHome: goString(si.exechome),
execCWD: goString(si.execcwd),
execUser: goString(si.execuser),
}
}
return rawJobArrayToModels(uint64(jobArray.jobid), uint64(jobArray.array_size), rd)
}
// cJobToModel extracts C xce_job_t fields into a rawJobData and maps it to JobInfo.
func cJobToModel(job *C.xce_job_t) *models.JobInfo {
r := rawJobData{