5.5 KiB
Code Improvement Suggestions
Date: 2026-04-09
Based on current codebase state after all bug fixes.
1. Critical Bugs Still Present
1.1 queue_name filter added N times in GetJobsInfo (apiserver)
File: controllers/jobs.go ~line 226
Problem: The queue_name:in:(...) RBAC filter is appended inside the per-filter loop — once for every non-queue filter in the request. With 2 filters (e.g. job_status + submit_user), it gets added twice. Seen in server logs: queue_name:in:(normal,priority,short) appears twice in every query.
Fix: Move the accessQueues append outside the loop, after it completes.
// BEFORE (inside loop — adds queue_name once per non-queue filter)
for i, filter := range filters {
...
} else {
if len(accessQueues) > 0 {
filters = append(filters, ...) // BUG: runs N times
}
}
}
// AFTER (outside loop — adds once)
for i, filter := range filters { ... }
if len(accessQueues) > 0 && !queueFilterPresent {
filters = append(filters, utils.FilterCondition{
Field: "queue_name", Operator: "in",
Value: "(" + strings.Join(accessQueues, ",") + ")",
})
}
2. Design Issues
2.1 controllers/jobs.go is 3590 lines — needs splitting
The file handles job query, submit, control, restart, array jobs, and verbose output. Each is a distinct concern.
Suggested split:
controllers/
jobs_query.go — GetJobsInfo, GetJobByIDs (~600 lines)
jobs_submit.go — CreateJob, RestartJobByID (~500 lines)
jobs_control.go — ControlJob, ControlJobByID (~400 lines)
jobs_array.go — array job handling (~300 lines)
jobs_helpers.go — shared: parseArrayName, processJobData, etc.
2.2 RBAC permission check duplicated across controllers
GetRequireResourcePermission + accessQueues logic is copy-pasted in GetJobsInfo, ControlJob, ControlJobByID, RestartJobByID. Extract to a helper:
// controllers/rbac_helpers.go
func getJobAccessQueues(username string) ([]string, error) { ... }
func checkJobViewPermission(username, queueName string) error { ... }
2.3 xcecli/cmd/jobs/submit.go is 734 lines
JobSubmitter mixes CLI flag parsing, file upload, interactive job handling, and HTTP submission. Split:
cmd/jobs/
submit.go — cobra command + flag binding only
submitter.go — JobSubmitter struct + BuildJobRequest
submit_files.go — file upload logic (already in files.go ✓)
submit_interactive.go — interactive/TTY/xjob handling
3. Code Quality
3.1 ioutil.ReadFile deprecated (Go 1.16)
File: controllers/rbac_check.go:54
Fix: os.ReadFile(path) — one-line change, tracked as Q3.
3.2 Inconsistent error return convention
Some functions return (error, T), others return (T, error). Go convention is always (T, error).
// Non-standard (apiserver/controllers/rbac_check.go)
func GetRequireResourcePermission(...) (error, models.RolePermission)
// Should be
func GetRequireResourcePermission(...) (models.RolePermission, error)
This affects ~8 call sites in jobs.go that do err, perm := GetRequire....
3.3 Magic strings for domain types
"all", "self", "self_ug", "queue" appear as raw strings in multiple files. They're defined as constants in models/roles.go but not always used.
Fix: Replace all raw string comparisons with the constants (models.DomainTypeAll, etc.).
3.4 xcecli output printers have no shared time formatting
page_print_jobs.go now has parseJobTime/formatJobTime. The same pattern will be needed for start_time, end_time in other printers. Move to common/output/time.go.
4. Architecture
4.1 In-memory store has no persistence
After restart, all submitted jobs (non-builtin) are lost. The lib/pq PostgreSQL driver is already in go.mod. When backend integration happens, the notification handlers (OnJobCreated, etc.) should write through to PostgreSQL, not just the in-memory store.
4.2 UserRoleSeeder only implemented for config_file
After the startup fix, PAM/LDAP/OAuth2 providers don't implement AllUsers() — so after restart with those providers, users still need to re-login to get their roles. Consider storing user-role assignments in a persistent store (DB or file) so they survive restarts regardless of provider.
4.3 Rate limiter limiters map grows unboundedly
File: middleware/rate_limit.go
The per-endpoint limiters map is never evicted. Under normal operation with fixed routes this is fine, but worth noting for future dynamic routing.
5. Priority Order
quadrantChart
title Impact vs Effort
x-axis Low Effort --> High Effort
y-axis Low Impact --> High Impact
quadrant-1 Do First
quadrant-2 Plan
quadrant-3 Later
quadrant-4 Quick Wins
queue_name double-add: [0.10, 0.90]
ioutil fix: [0.05, 0.20]
error convention: [0.25, 0.60]
split jobs.go: [0.60, 0.70]
split submit.go: [0.50, 0.50]
RBAC helper: [0.35, 0.65]
time.go shared: [0.15, 0.35]
persistence: [0.80, 0.85]
UserRoleSeeder all providers: [0.30, 0.55]
Do immediately:
- Fix
queue_namedouble-add bug inGetJobsInfo(10 min, high impact) ioutil→os.ReadFile(1 min)
Next sprint:
3. Fix (error, T) → (T, error) return convention
4. Extract RBAC helpers to reduce duplication in jobs.go
5. Split jobs.go into focused files
Backend integration phase:
6. PostgreSQL persistence via notification handlers
7. UserRoleSeeder for PAM/LDAP providers