refactor: optimize job listing and counting logic for concurrency safety. Separate tenant filtering and status checks to reduce lock contention, improving performance and ensuring accurate job status retrieval.
CI / changes (push) Successful in 14s
CI / openapi (push) Has been skipped
CI / go (push) Successful in 1m12s
CI / docker-web (deploy/docker/evobgp-web/Dockerfile, , evobgp-web) (push) Has been skipped
CI / docker-web (deploy/docker/evobgp-web/Dockerfile, evobgp-all, evobgp-web-all) (push) Has been skipped
CI / docker-bird (push) Has been skipped
CI / bird2 (push) Successful in 20s
CI / docker-go-prime (push) Successful in 29s
CI / docker-go (deploy/docker/evobgp-agent/Dockerfile, , evobgp-agent) (push) Successful in 1m19s
CI / docker-go (evobgp-all, 1, deploy/docker/gobinary/Dockerfile, , evobgp-all) (push) Successful in 2m54s
CI / docker-go (evobgp-api, 1, deploy/docker/gobinary/Dockerfile, , evobgp-api) (push) Successful in 1m49s
CI / docker-go (evobgp-deploy, 0, deploy/docker/gobinary/Dockerfile, , evobgp-deploy) (push) Successful in 1m47s
CI / docker-go (evobgp-ingest, 0, deploy/docker/gobinary/Dockerfile, , evobgp-ingest) (push) Successful in 1m45s
CI / docker-go (evobgp-node, 0, deploy/docker/gobinary/Dockerfile, , evobgp-node) (push) Successful in 1m14s
CI / docker-go (evobgp-render, 0, deploy/docker/gobinary/Dockerfile, , evobgp-render) (push) Successful in 1m30s
CI / docker-go (evobgp-scheduler, 0, deploy/docker/gobinary/Dockerfile, , evobgp-scheduler) (push) Successful in 1m26s

This commit is contained in:
Denozordec
2026-04-07 00:03:46 +07:00
parent 149fb0a18c
commit d83efe24ef
+30 -13
View File
@@ -229,22 +229,32 @@ func (r *Registry) List(tenantID, statusFilter, kindFilter, cursor string, limit
if limit <= 0 {
limit = 50
}
// Build candidate set under r.mu, but read mutable status fields (j.Status) outside
// of r.mu so we don't race with Job.mu-protected writes (MarkRunning/Succeed/Fail).
r.mu.RLock()
var all []*Job
all := make([]*Job, 0, len(r.byID))
for _, j := range r.byID {
if j.TenantID != tenantID {
continue
if j.TenantID == tenantID {
all = append(all, j)
}
if statusFilter != "" && j.Status != statusFilter {
continue
}
if kindFilter != "" && j.Kind != kindFilter {
continue
}
all = append(all, j)
}
r.mu.RUnlock()
// Apply filters outside of r.mu to synchronize with Job.mu.
if statusFilter != "" || kindFilter != "" {
filtered := all[:0]
for _, j := range all {
if kindFilter != "" && j.Kind != kindFilter {
continue
}
if statusFilter != "" && j.statusLocked() != statusFilter {
continue
}
filtered = append(filtered, j)
}
all = filtered
}
sort.Slice(all, func(i, j int) bool {
return all[i].CreatedAt.After(all[j].CreatedAt)
})
@@ -273,9 +283,9 @@ func (r *Registry) CountOtherActiveModuleRefresh(tenantID, excludeJobID string)
if r == nil {
return 0
}
// Two-phase: snapshot job pointers under r.mu, then read j.Status under Job.mu.
r.mu.RLock()
defer r.mu.RUnlock()
n := 0
candidates := make([]*Job, 0, 8)
for _, j := range r.byID {
if j.TenantID != tenantID || j.Kind != KindModuleRefresh {
continue
@@ -283,7 +293,14 @@ func (r *Registry) CountOtherActiveModuleRefresh(tenantID, excludeJobID string)
if j.ID == excludeJobID {
continue
}
if j.Status == StatusQueued || j.Status == StatusRunning {
candidates = append(candidates, j)
}
r.mu.RUnlock()
n := 0
for _, j := range candidates {
st := j.statusLocked()
if st == StatusQueued || st == StatusRunning {
n++
}
}