mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
fix(coderd/database): exclude canceled jobs in queue position (#15835)
When calculating the queue position in `GetProvisionerJobsByIDsWithQueuePosition` we only counted jobs with `started_at = NULL`. This is misleading, as it allows canceling or canceled jobs to take up rows in the computed queue position, giving an impression that the queue is larger than it really is. This modifies the query to also exclude jobs with a null `canceled_at`, `completed_at`, or `error` field for the purposes of calculating the queue position, and also adds a test to validate this behaviour. (Note: due to the behaviour of `dbgen.ProvisionerJob` with `dbmem` I had to use other proxy methods to validate the corresponding dbmem implementation.) --------- Co-authored-by: Mathias Fredriksson <mafredri@gmail.com>
This commit is contained in:
co-authored by
Mathias Fredriksson
parent
edb0cb155f
commit
36c2cf8a40
@@ -3804,35 +3804,92 @@ func (q *FakeQuerier) GetProvisionerJobsByIDsWithQueuePosition(_ context.Context
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
|
||||
jobs := make([]database.GetProvisionerJobsByIDsWithQueuePositionRow, 0)
|
||||
queuePosition := int64(1)
|
||||
// WITH pending_jobs AS (
|
||||
// SELECT
|
||||
// id, created_at
|
||||
// FROM
|
||||
// provisioner_jobs
|
||||
// WHERE
|
||||
// started_at IS NULL
|
||||
// AND
|
||||
// canceled_at IS NULL
|
||||
// AND
|
||||
// completed_at IS NULL
|
||||
// AND
|
||||
// error IS NULL
|
||||
// ),
|
||||
type pendingJobRow struct {
|
||||
ID uuid.UUID
|
||||
CreatedAt time.Time
|
||||
}
|
||||
pendingJobs := make([]pendingJobRow, 0)
|
||||
for _, job := range q.provisionerJobs {
|
||||
for _, id := range ids {
|
||||
if id == job.ID {
|
||||
// clone the Tags before appending, since maps are reference types and
|
||||
// we don't want the caller to be able to mutate the map we have inside
|
||||
// dbmem!
|
||||
job.Tags = maps.Clone(job.Tags)
|
||||
job := database.GetProvisionerJobsByIDsWithQueuePositionRow{
|
||||
ProvisionerJob: job,
|
||||
}
|
||||
if !job.ProvisionerJob.StartedAt.Valid {
|
||||
job.QueuePosition = queuePosition
|
||||
}
|
||||
jobs = append(jobs, job)
|
||||
break
|
||||
}
|
||||
}
|
||||
if !job.StartedAt.Valid {
|
||||
queuePosition++
|
||||
if job.StartedAt.Valid ||
|
||||
job.CanceledAt.Valid ||
|
||||
job.CompletedAt.Valid ||
|
||||
job.Error.Valid {
|
||||
continue
|
||||
}
|
||||
pendingJobs = append(pendingJobs, pendingJobRow{
|
||||
ID: job.ID,
|
||||
CreatedAt: job.CreatedAt,
|
||||
})
|
||||
}
|
||||
for _, job := range jobs {
|
||||
if !job.ProvisionerJob.StartedAt.Valid {
|
||||
// Set it to the max position!
|
||||
job.QueueSize = queuePosition
|
||||
}
|
||||
|
||||
// queue_position AS (
|
||||
// SELECT
|
||||
// id,
|
||||
// ROW_NUMBER() OVER (ORDER BY created_at ASC) AS queue_position
|
||||
// FROM
|
||||
// pending_jobs
|
||||
// ),
|
||||
slices.SortFunc(pendingJobs, func(a, b pendingJobRow) int {
|
||||
c := a.CreatedAt.Compare(b.CreatedAt)
|
||||
return c
|
||||
})
|
||||
|
||||
queuePosition := make(map[uuid.UUID]int64)
|
||||
for idx, pj := range pendingJobs {
|
||||
queuePosition[pj.ID] = int64(idx + 1)
|
||||
}
|
||||
|
||||
// queue_size AS (
|
||||
// SELECT COUNT(*) AS count FROM pending_jobs
|
||||
// ),
|
||||
queueSize := len(pendingJobs)
|
||||
|
||||
// SELECT
|
||||
// sqlc.embed(pj),
|
||||
// COALESCE(qp.queue_position, 0) AS queue_position,
|
||||
// COALESCE(qs.count, 0) AS queue_size
|
||||
// FROM
|
||||
// provisioner_jobs pj
|
||||
// LEFT JOIN
|
||||
// queue_position qp ON pj.id = qp.id
|
||||
// LEFT JOIN
|
||||
// queue_size qs ON TRUE
|
||||
// WHERE
|
||||
// pj.id IN (...)
|
||||
jobs := make([]database.GetProvisionerJobsByIDsWithQueuePositionRow, 0)
|
||||
for _, job := range q.provisionerJobs {
|
||||
if !slices.Contains(ids, job.ID) {
|
||||
continue
|
||||
}
|
||||
// clone the Tags before appending, since maps are reference types and
|
||||
// we don't want the caller to be able to mutate the map we have inside
|
||||
// dbmem!
|
||||
job.Tags = maps.Clone(job.Tags)
|
||||
job := database.GetProvisionerJobsByIDsWithQueuePositionRow{
|
||||
// sqlc.embed(pj),
|
||||
ProvisionerJob: job,
|
||||
// COALESCE(qp.queue_position, 0) AS queue_position,
|
||||
QueuePosition: queuePosition[job.ID],
|
||||
// COALESCE(qs.count, 0) AS queue_size
|
||||
QueueSize: int64(queueSize),
|
||||
}
|
||||
jobs = append(jobs, job)
|
||||
}
|
||||
|
||||
return jobs, nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user