forked from lei.chen/myxce
improve frontend jobid display
This commit is contained in:
parent
3ed7ef011a
commit
d6008ead99
6 changed files with 305 additions and 41 deletions
148
src/frontend/xcecli/common/format/jobid.go
Normal file
148
src/frontend/xcecli/common/format/jobid.go
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
package format
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// 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 | 5 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 (== N_ARRAY_IDX_BITS)
|
||||
nSequenceBits = 20 // N_JOBID_BITS(64) - N_CLUSTER_ID_BITS(6) - N_APISERVER_ID_BITS(4) - N_TIMESTAMP_BITS(32) - N_TAG_BITS(2) = 20
|
||||
tagJobArray = 0x01
|
||||
)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// FormatJobID decomposes a 64-bit packed job ID and formats it as a
|
||||
// human-readable string matching the C fmt_jobid() format in src/lib/utils.c.
|
||||
// - Regular job: "{cluster}-{apiserver}-{timestamp}-{sequence}"
|
||||
// - Array child: "{cluster}-{apiserver}-{timestamp}-{base}[{idx}]"
|
||||
func FormatJobID(uniqueJobID int64) string {
|
||||
id := uint64(uniqueJobID)
|
||||
clusterID := extractBits(id, clusterIDShift, nClusterIDBits)
|
||||
apiserverID := extractBits(id, apiserverIDShift, nApiserverIDBits)
|
||||
timestamp := extractBits(id, timestampShift, nTimestampBits)
|
||||
|
||||
if (extractBits(id, tagShift, nTagBits) & tagJobArray) != 0 {
|
||||
// arrayBase mirrors EXTRACT_ARRAY_JOB: the seq_high bits shifted back to their
|
||||
// original position, matching C's fmt_jobid_ex output exactly.
|
||||
arrayBase := extractBits(id, sequenceHighShift, nSequenceBits-nArrayIdxBits) << uint(sequenceHighShift)
|
||||
arrayIdx := extractBits(id, 0, nArrayIdxBits)
|
||||
return fmt.Sprintf("%d-%d-%d-%d[%d]",
|
||||
clusterID, apiserverID, timestamp, arrayBase, arrayIdx)
|
||||
}
|
||||
sequence := extractBits(id, 0, nSequenceBits)
|
||||
return fmt.Sprintf("%d-%d-%d-%d",
|
||||
clusterID, apiserverID, timestamp, sequence)
|
||||
}
|
||||
|
||||
// IsArrayJob returns true if the 64-bit packed job ID has the array job tag set.
|
||||
func IsArrayJob(uniqueJobID int64) bool {
|
||||
return extractBits(uint64(uniqueJobID), tagShift, nTagBits)&tagJobArray != 0
|
||||
}
|
||||
|
||||
// DecomposeJobID splits a 64-bit packed job ID into its component fields.
|
||||
func DecomposeJobID(uniqueJobID int64) (clusterID, apiserverID, timestamp, sequence uint32, isArray bool, arrayIdx uint32) {
|
||||
id := uint64(uniqueJobID)
|
||||
clusterID = uint32(extractBits(id, clusterIDShift, nClusterIDBits))
|
||||
apiserverID = uint32(extractBits(id, apiserverIDShift, nApiserverIDBits))
|
||||
timestamp = uint32(extractBits(id, timestampShift, nTimestampBits))
|
||||
isArray = extractBits(id, tagShift, nTagBits)&tagJobArray != 0
|
||||
sequence = uint32(extractBits(id, 0, nSequenceBits))
|
||||
arrayIdx = uint32(extractBits(id, 0, nArrayIdxBits))
|
||||
return
|
||||
}
|
||||
|
||||
// MakeJobID packs the components into a 64-bit job ID, mirroring MAKE_JOBID in src/include/utils.h.
|
||||
func MakeJobID(clusterID, apiserverID, timestamp uint64, tag, sequence uint64) uint64 {
|
||||
return (clusterID << clusterIDShift) |
|
||||
(apiserverID << apiserverIDShift) |
|
||||
(timestamp << timestampShift) |
|
||||
(tag << tagShift) |
|
||||
sequence
|
||||
}
|
||||
|
||||
var (
|
||||
// regexJobID matches "{int}-{int}-{int}-{int}" — mirrors REGEX_JOBID in config.h.
|
||||
regexJobID = regexp.MustCompile(`^\s*(\d+)-(\d+)-(\d+)-(\d+)\s*$`)
|
||||
// regexJobArrayID matches "{int}-{int}-{int}-{int}[{int}]" — mirrors REGEX_JOBARRAY_ID in config.h.
|
||||
regexJobArrayID = regexp.MustCompile(`^\s*(\d+)-(\d+)-(\d+)-(\d+)\[(\d+)\]\s*$`)
|
||||
)
|
||||
|
||||
// ParseJobID parses a human-readable job ID string into a packed 64-bit job ID,
|
||||
// mirroring parse_jobid() in src/lib/utils.c:482.
|
||||
// Accepts: "0-0-12874529-0" (regular) or "0-0-12874529-0[5]" (array child).
|
||||
func ParseJobID(s string) (uint64, error) {
|
||||
if m := regexJobArrayID.FindStringSubmatch(s); m != nil {
|
||||
clusterID, _ := strconv.ParseUint(m[1], 10, 64)
|
||||
apiserverID, _ := strconv.ParseUint(m[2], 10, 64)
|
||||
timestamp, _ := strconv.ParseUint(m[3], 10, 64)
|
||||
idJob, _ := strconv.ParseUint(m[4], 10, 64)
|
||||
idIdx, _ := strconv.ParseUint(m[5], 10, 64)
|
||||
return MakeJobID(clusterID, apiserverID, timestamp, tagJobArray, idJob+idIdx), nil
|
||||
}
|
||||
if m := regexJobID.FindStringSubmatch(s); m != nil {
|
||||
clusterID, _ := strconv.ParseUint(m[1], 10, 64)
|
||||
apiserverID, _ := strconv.ParseUint(m[2], 10, 64)
|
||||
timestamp, _ := strconv.ParseUint(m[3], 10, 64)
|
||||
idJob, _ := strconv.ParseUint(m[4], 10, 64)
|
||||
return MakeJobID(clusterID, apiserverID, timestamp, 0, idJob), nil
|
||||
}
|
||||
return 0, fmt.Errorf("invalid job ID format: %q", s)
|
||||
}
|
||||
|
||||
// ExtractBaseJobID clears the tag and array_idx bits, returning the base job ID
|
||||
// that the API server stores in the job_id column.
|
||||
func ExtractBaseJobID(packed uint64) uint64 {
|
||||
id := packed &^ (uint64((1<<nTagBits)-1) << uint(tagShift)) // clear tag bits
|
||||
id = id &^ uint64((1<<nArrayIdxBits)-1) // clear array_idx bits
|
||||
return id
|
||||
}
|
||||
|
||||
// FormatArrayChildID formats a job ID from a base job ID and array index.
|
||||
// Used when only base+index are available (e.g. JobControlResponseItem).
|
||||
func FormatArrayChildID(baseJobID int64, arrayIdx int) string {
|
||||
if arrayIdx == 0 {
|
||||
return FormatJobID(baseJobID)
|
||||
}
|
||||
uniqueID := uint64(baseJobID) | (uint64(tagJobArray) << uint(tagShift)) | uint64(arrayIdx)
|
||||
return FormatJobID(int64(uniqueID))
|
||||
}
|
||||
|
||||
// ToAPIJobID converts a fmt_jobid-format string to the numeric form the API
|
||||
// server expects ("{int}" or "{int}[{idx}]"). Plain integers pass through unchanged.
|
||||
func ToAPIJobID(s string) (string, error) {
|
||||
if regexJobArrayID.MatchString(s) || regexJobID.MatchString(s) {
|
||||
packed, err := ParseJobID(s)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if IsArrayJob(int64(packed)) {
|
||||
baseID := ExtractBaseJobID(packed)
|
||||
arrayIdx := extractBits(packed, 0, nArrayIdxBits)
|
||||
return fmt.Sprintf("%d[%d]", baseID, arrayIdx), nil
|
||||
}
|
||||
return fmt.Sprintf("%d", packed), nil
|
||||
}
|
||||
return s, nil // already plain int or int[idx]
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue