feat(job-array-one-based-index): control by identity validates first and dispatches once (task 7.4)

ControlJobByID parses every selection with ParseIdentitySelection before
any store access (first rejection: 400, nothing dispatched), collects
the jobs to act on de-duplicated by unique_job_id plus one not-found
result per missing number per selection (carrying the would-be element
ID), validates each job once after the loop and dispatches each
accepted job once. foundJob gains an explicit-index flag so
<array-id>[0] matches only element 0 and never falls back to the whole
array or an ordinary job. Fixes bug-1899 (earlier selections were
re-dispatched for every later one).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Rongsong Shen 2026-10-01 12:29:19 +00:00
parent c3962e8f61
commit 4020740208
Signed by untrusted user: rshen
GPG key ID: 80533536B0EB651C
4 changed files with 475 additions and 241 deletions

View file

@ -2,6 +2,7 @@ package controllers
import (
"errors"
"apiserver/backend"
"apiserver/models"
"apiserver/store"
"apiserver/utils"
@ -179,13 +180,17 @@ func TestValidateJobControl_RejectsNonKillableStatus(t *testing.T) {
func TestFoundJob_MatchesRealArrayIdxType(t *testing.T) {
resetJobSubmitState()
// an array element: its unique_job_id carries the array marking
const key = uint64(2)<<40 | uint64(1)<<20
elem := backend.MakeElementID(key, 1)
jobInfo := models.JobInfo{
JobID: 1788186846,
ArrayIdx: 1,
Unique_JobID: int64(elem),
JobID: int64(key),
ArrayIdx: 1,
}
orderMap := store.StructToOrderMap(jobInfo)
found, err := foundJob(1788186846, 1, []utils.OrderMap{orderMap})
found, err := foundJob(int64(key), 1, true, []utils.OrderMap{orderMap})
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
@ -194,6 +199,49 @@ func TestFoundJob_MatchesRealArrayIdxType(t *testing.T) {
}
}
// TestFoundJob_ExplicitIndex (5.4, 5.6, 8.2): an explicit element number
// matches only array elements with that number -- never an ordinary job with
// array_idx 0 or the whole array -- while no element part matches every row
// with that job_id.
func TestFoundJob_ExplicitIndex(t *testing.T) {
const key = uint64(2)<<40 | uint64(1)<<20
row := func(uid uint64, idx int) utils.OrderMap {
return store.StructToOrderMap(models.JobInfo{Unique_JobID: int64(uid), JobID: int64(key), ArrayIdx: idx})
}
ordinary := row(key, 0) // an ordinary job sharing the number
e1 := row(backend.MakeElementID(key, 1), 1)
e2 := row(backend.MakeElementID(key, 2), 2)
withZero := []utils.OrderMap{ordinary, e1, e2, row(backend.MakeElementID(key, 0), 0)}
noZero := []utils.OrderMap{ordinary, e1, e2}
uids := func(rows []utils.OrderMap) []int64 {
out := []int64{}
for _, r := range rows {
out = append(out, r.Data["unique_job_id"].(int64))
}
return out
}
if got, err := foundJob(int64(key), 0, true, noZero); err == nil {
t.Errorf("[0] without element 0 matched %v, want not found", uids(got))
}
got, err := foundJob(int64(key), 0, true, withZero)
if err != nil || len(got) != 1 || uint64(got[0].Data["unique_job_id"].(int64)) != backend.MakeElementID(key, 0) {
t.Errorf("[0] with a pre-upgrade element 0 = %v, %v; want that element alone", uids(got), err)
}
got, err = foundJob(int64(key), 2, true, noZero)
if err != nil || len(got) != 1 || uint64(got[0].Data["unique_job_id"].(int64)) != backend.MakeElementID(key, 2) {
t.Errorf("[2] = %v, %v; want element 2 alone", uids(got), err)
}
if got, err := foundJob(int64(key), 3, true, noZero); err == nil {
t.Errorf("[3] matched %v, want not found", uids(got))
}
got, err = foundJob(int64(key), 0, false, noZero)
if err != nil || len(got) != 3 {
t.Errorf("bare = %v, %v; want every row with that job_id", uids(got), err)
}
}
// TestPreCheckJobRequest_RejectsMalformedResreq (bug-1744): a resource request
// the syntax check rejects must fail the submission instead of being accepted
// and later silently dropped by the scheduler.