fix job array display issue
This commit is contained in:
parent
d9b5c361c2
commit
960ef62342
5 changed files with 290 additions and 15 deletions
|
|
@ -250,7 +250,8 @@ func SubmitJob(job *models.JobInfo, arraySize int) error {
|
|||
}
|
||||
if outJobID != 0 {
|
||||
job.Unique_JobID = int64(outJobID)
|
||||
job.JobID = int64(outJobID)
|
||||
job.JobID = int64(ExtractArrayBaseID(uint64(outJobID)))
|
||||
job.ArrayIdx = ExtractArrayIdx(uint64(outJobID))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,76 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
// JobID bit layout constants — mirrors src/include/config.h.
|
||||
//
|
||||
// |63 62|61 56|55 52|51 20|19 15|14 0|
|
||||
// | TAG | CLUSTER_ID | APISERVER_ID| TIMESTAMP | SEQ_HIGH | ARRAY_IDX |
|
||||
// | 2 bits | 6 bits | 4 bits | 32 bits | 15 bits | 15 bits |
|
||||
const (
|
||||
nTagBits = 2
|
||||
tagShift = 62
|
||||
nClusterIDBits = 6
|
||||
clusterIDShift = 56
|
||||
nApiserverIDBits = 4
|
||||
apiserverIDShift = 52
|
||||
nTimestampBits = 32
|
||||
timestampShift = 20
|
||||
nArrayIdxBits = 15
|
||||
sequenceHighShift = 15 // mirrors SEQUENCE_HIGH_SHIFT in config.h
|
||||
nSequenceBits = 30 // mirrors N_SEQUENCE_BITS in config.h
|
||||
tagJobArray = 0x01 // mirrors TAG_JOB_ARRAY in config.h
|
||||
)
|
||||
|
||||
// extractBits extracts a bit field from id at the given shift position.
|
||||
// Mirrors EXTRACT_BITS(id, shift, bits) in src/include/utils.h.
|
||||
func extractBits(id uint64, shift, bits int) uint64 {
|
||||
mask := uint64((1 << bits) - 1)
|
||||
return (id >> shift) & mask
|
||||
}
|
||||
|
||||
// extractIDTag extracts the 2-bit tag field (bits 62-63).
|
||||
// Mirrors EXTRACT_ID_TAG in src/include/utils.h.
|
||||
func extractIDTag(id uint64) uint64 {
|
||||
return extractBits(id, tagShift, nTagBits)
|
||||
}
|
||||
|
||||
// ExtractArrayBaseID extracts the base (family) job ID from a 64-bit encoded
|
||||
// jobid. Mirrors C's array_jobid() in src/lib/utils.c and EXTRACT_ARRAY_BASEID
|
||||
// in src/include/utils.h.
|
||||
// - For non-array jobs (tag=0x00): returns id unchanged.
|
||||
// - For array children (tag=0x01): returns the shared base ID (tag=0, idx=0).
|
||||
func ExtractArrayBaseID(id uint64) uint64 {
|
||||
if extractIDTag(id) == 0x00 {
|
||||
return id
|
||||
}
|
||||
clusterID := extractBits(id, clusterIDShift, nClusterIDBits)
|
||||
apiserverID := extractBits(id, apiserverIDShift, nApiserverIDBits)
|
||||
timestamp := extractBits(id, timestampShift, nTimestampBits)
|
||||
// EXTRACT_ARRAY_JOB: high bits of sequence (mirrors config.h SEQUENCE_HIGH_SHIFT)
|
||||
arrayJob := extractBits(id, sequenceHighShift, nSequenceBits-nArrayIdxBits) << sequenceHighShift
|
||||
return (clusterID << clusterIDShift) |
|
||||
(apiserverID << apiserverIDShift) |
|
||||
(timestamp << timestampShift) |
|
||||
arrayJob // tag=0, idx=0
|
||||
}
|
||||
|
||||
// ExtractArrayIdx extracts the array index from a 64-bit encoded jobid.
|
||||
// Mirrors C's array_idx() in src/lib/utils.c and EXTRACT_ARRAY_IDX in
|
||||
// src/include/utils.h.
|
||||
// Returns 0 for non-array jobs (tag=0x00).
|
||||
func ExtractArrayIdx(id uint64) int {
|
||||
if extractIDTag(id) == 0x00 {
|
||||
return 0
|
||||
}
|
||||
return int(extractBits(id, 0, nArrayIdxBits))
|
||||
}
|
||||
|
||||
// IsArrayJob reports whether the 64-bit encoded jobid belongs to an array job
|
||||
// (tag == TAG_JOB_ARRAY, 0x01). Returns false for non-array jobs (tag=0x00).
|
||||
func IsArrayJob(id uint64) bool {
|
||||
return extractIDTag(id) == tagJobArray
|
||||
}
|
||||
|
||||
// rawStartInfo mirrors the C job_startinfo_t fields used by the Go model.
|
||||
type rawStartInfo struct {
|
||||
cmdline string
|
||||
|
|
@ -77,9 +147,11 @@ type rawResourceData struct {
|
|||
// rawJobToModel maps a rawJobData to a models.JobInfo.
|
||||
// This pure-Go function contains the field-mapping logic and is tested without CGO.
|
||||
func rawJobToModel(r rawJobData) *models.JobInfo {
|
||||
raw := uint64(r.jobID)
|
||||
info := &models.JobInfo{
|
||||
Unique_JobID: r.jobID,
|
||||
JobID: r.jobID,
|
||||
JobID: int64(ExtractArrayBaseID(raw)),
|
||||
ArrayIdx: ExtractArrayIdx(raw),
|
||||
JobStatus: r.state,
|
||||
RunTime: r.runTime,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -197,3 +197,193 @@ func TestRawResourceToModel_BasicFields(t *testing.T) {
|
|||
t.Errorf("Interval: got %d, want 60", info.Interval)
|
||||
}
|
||||
}
|
||||
|
||||
// makeJobID mirrors the C MAKE_JOBID macro in src/include/utils.h.
|
||||
// Used to construct known 64-bit job IDs for testing.
|
||||
func makeJobID(cluster, apiserver, timestamp, tag, sequence uint64) uint64 {
|
||||
return (cluster << clusterIDShift) |
|
||||
(apiserver << apiserverIDShift) |
|
||||
(timestamp << timestampShift) |
|
||||
(tag << tagShift) |
|
||||
sequence
|
||||
}
|
||||
|
||||
func TestExtractBits(t *testing.T) {
|
||||
// 0b1111 shifted left by 4 = 0b11110000 = 240
|
||||
id := uint64(0xF0)
|
||||
if got := extractBits(id, 4, 4); got != 0xF {
|
||||
t.Errorf("extractBits(0xF0, 4, 4): got %d, want 15", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractIDTag_NonArray(t *testing.T) {
|
||||
id := makeJobID(1, 0, 1700000000, 0, 5)
|
||||
if tag := extractIDTag(id); tag != 0x00 {
|
||||
t.Errorf("tag for non-array job: got %d, want 0", tag)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractIDTag_Array(t *testing.T) {
|
||||
id := makeJobID(1, 0, 1700000000, tagJobArray, 5)
|
||||
if tag := extractIDTag(id); tag != tagJobArray {
|
||||
t.Errorf("tag for array job: got %d, want %d", tag, tagJobArray)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractArrayBaseID_NonArray(t *testing.T) {
|
||||
// Non-array job: base ID should equal the original ID
|
||||
id := makeJobID(1, 0, 1700000000, 0, 5)
|
||||
base := ExtractArrayBaseID(id)
|
||||
if base != id {
|
||||
t.Errorf("non-array base ID should equal original: got %d, want %d", base, id)
|
||||
}
|
||||
idx := ExtractArrayIdx(id)
|
||||
if idx != 0 {
|
||||
t.Errorf("non-array index should be 0: got %d", idx)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractArrayBaseID_ArrayChild(t *testing.T) {
|
||||
// Array job: cluster=1, apiserver=0, timestamp=1700000000
|
||||
// The sequence for array jobs has the "job number" in high bits.
|
||||
// sequence = jobNumber << sequenceHighShift + arrayIdx
|
||||
// e.g. jobNumber=2, arrayIdx=3 → sequence = 2<<15 + 3 = 65539
|
||||
jobNumber := uint64(2)
|
||||
arrayIdx := uint64(3)
|
||||
sequence := jobNumber<<sequenceHighShift + arrayIdx
|
||||
id := makeJobID(1, 0, 1700000000, tagJobArray, sequence)
|
||||
|
||||
base := ExtractArrayBaseID(id)
|
||||
// Base ID should have tag=0, same cluster/apiserver/timestamp, and arrayJob in sequence
|
||||
expectedBase := makeJobID(1, 0, 1700000000, 0, jobNumber<<sequenceHighShift)
|
||||
if base != expectedBase {
|
||||
t.Errorf("array base ID mismatch: got %d, want %d", base, expectedBase)
|
||||
}
|
||||
|
||||
idx := ExtractArrayIdx(id)
|
||||
if idx != int(arrayIdx) {
|
||||
t.Errorf("array index: got %d, want %d", idx, arrayIdx)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractArrayIdx_MaxIndex(t *testing.T) {
|
||||
// Maximum array index = 32767 (2^15 - 1)
|
||||
maxIdx := uint64((1 << nArrayIdxBits) - 1)
|
||||
sequence := uint64(1)<<sequenceHighShift + maxIdx
|
||||
id := makeJobID(1, 0, 1700000000, tagJobArray, sequence)
|
||||
|
||||
idx := ExtractArrayIdx(id)
|
||||
if idx != int(maxIdx) {
|
||||
t.Errorf("max array index: got %d, want %d", idx, maxIdx)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractArrayBaseID_RoundTrip(t *testing.T) {
|
||||
// ExtractArrayBaseID and ExtractArrayIdx together should preserve enough
|
||||
// information to reconstruct the original ID.
|
||||
// Reconstruction requires re-encoding with tag=TAG_JOB_ARRAY.
|
||||
jobNumber := uint64(7)
|
||||
arrayIdx := uint64(42)
|
||||
sequence := jobNumber<<sequenceHighShift + arrayIdx
|
||||
id := makeJobID(1, 0, 1700000000, tagJobArray, sequence)
|
||||
|
||||
base := ExtractArrayBaseID(id)
|
||||
idx := ExtractArrayIdx(id)
|
||||
|
||||
// Reconstruct: base ID sequence + array index, with array tag set
|
||||
reconstructedSeq := (extractBits(base, sequenceHighShift, nSequenceBits-nArrayIdxBits) << sequenceHighShift) + uint64(idx)
|
||||
reconstructed := makeJobID(
|
||||
extractBits(base, clusterIDShift, nClusterIDBits),
|
||||
extractBits(base, apiserverIDShift, nApiserverIDBits),
|
||||
extractBits(base, timestampShift, nTimestampBits),
|
||||
tagJobArray,
|
||||
reconstructedSeq,
|
||||
)
|
||||
if reconstructed != id {
|
||||
t.Errorf("round-trip failed: reconstructed %d, want %d", reconstructed, id)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRawJobToModel_NonArrayJob(t *testing.T) {
|
||||
// Non-array job: JobID == Unique_JobID, ArrayIdx == 0
|
||||
id := makeJobID(1, 0, 1700000000, 0, 5)
|
||||
r := rawJobData{jobID: int64(id), state: 2}
|
||||
info := rawJobToModel(r)
|
||||
|
||||
if info.Unique_JobID != int64(id) {
|
||||
t.Errorf("Unique_JobID: got %d, want %d", info.Unique_JobID, id)
|
||||
}
|
||||
if info.JobID != int64(id) {
|
||||
t.Errorf("JobID for non-array should equal Unique_JobID: got %d, want %d", info.JobID, id)
|
||||
}
|
||||
if info.ArrayIdx != 0 {
|
||||
t.Errorf("ArrayIdx for non-array should be 0: got %d", info.ArrayIdx)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRawJobToModel_ArrayChild(t *testing.T) {
|
||||
// Array child: JobID == baseID, ArrayIdx == arrayIdx
|
||||
jobNumber := uint64(2)
|
||||
arrayIdx := uint64(5)
|
||||
sequence := jobNumber<<sequenceHighShift + arrayIdx
|
||||
id := makeJobID(1, 0, 1700000000, tagJobArray, sequence)
|
||||
baseID := makeJobID(1, 0, 1700000000, 0, jobNumber<<sequenceHighShift)
|
||||
|
||||
r := rawJobData{jobID: int64(id), state: 2}
|
||||
info := rawJobToModel(r)
|
||||
|
||||
if info.Unique_JobID != int64(id) {
|
||||
t.Errorf("Unique_JobID: got %d, want %d", info.Unique_JobID, id)
|
||||
}
|
||||
if info.JobID != int64(baseID) {
|
||||
t.Errorf("JobID for array child should be base ID: got %d, want %d", info.JobID, baseID)
|
||||
}
|
||||
if info.ArrayIdx != int(arrayIdx) {
|
||||
t.Errorf("ArrayIdx for array child: got %d, want %d", info.ArrayIdx, arrayIdx)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRawJobToModel_ArrayChildrenShareBaseID(t *testing.T) {
|
||||
// All array children should share the same base JobID
|
||||
jobNumber := uint64(3)
|
||||
baseID := makeJobID(1, 0, 1700000000, 0, jobNumber<<sequenceHighShift)
|
||||
|
||||
for idx := 0; idx < 5; idx++ {
|
||||
sequence := jobNumber<<sequenceHighShift + uint64(idx)
|
||||
id := makeJobID(1, 0, 1700000000, tagJobArray, sequence)
|
||||
r := rawJobData{jobID: int64(id)}
|
||||
info := rawJobToModel(r)
|
||||
|
||||
if info.JobID != int64(baseID) {
|
||||
t.Errorf("array child %d: JobID %d should equal base ID %d", idx, info.JobID, baseID)
|
||||
}
|
||||
if info.ArrayIdx != idx {
|
||||
t.Errorf("array child %d: ArrayIdx got %d, want %d", idx, info.ArrayIdx, idx)
|
||||
}
|
||||
if info.Unique_JobID != int64(id) {
|
||||
t.Errorf("array child %d: Unique_JobID should be unique per child", idx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsArrayJob(t *testing.T) {
|
||||
// Non-array job (tag=0x00): IsArrayJob returns false
|
||||
nonArrayID := makeJobID(1, 0, 1700000000, 0, 5)
|
||||
if IsArrayJob(nonArrayID) {
|
||||
t.Errorf("non-array job should not be an array job")
|
||||
}
|
||||
|
||||
// Array child (tag=0x01): IsArrayJob returns true
|
||||
arrayID := makeJobID(1, 0, 1700000000, tagJobArray, 5)
|
||||
if !IsArrayJob(arrayID) {
|
||||
t.Errorf("array job child should be an array job")
|
||||
}
|
||||
|
||||
// Array child with idx=0: IsArrayJob returns true
|
||||
jobNumber := uint64(2)
|
||||
sequence := jobNumber<<sequenceHighShift + 0 // arrayIdx=0
|
||||
arrayIdx0ID := makeJobID(1, 0, 1700000000, tagJobArray, sequence)
|
||||
if !IsArrayJob(arrayIdx0ID) {
|
||||
t.Errorf("array child with idx=0 should still be an array job")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue