flamenco/internal/manager/persistence/jobs_blocklist.go
Sybren A. Stüvel 8513e2fdc8 Manager: replace GORM 'now' function with our own implementation
GORM implicitly sets 'created at', 'updated at' and 'deleted at' timestamps
to 'now' by calling a 'now function'. This is now implemented by Flamenco
directly, instead of relying on GORM.

Ref: #104305
2024-09-26 22:38:17 +02:00

143 lines
4.4 KiB
Go

package persistence
// SPDX-License-Identifier: GPL-3.0-or-later
import (
"context"
"math"
"time"
"projects.blender.org/studio/flamenco/internal/manager/persistence/sqlc"
)
// JobBlock keeps track of which Worker is not allowed to run which task type on which job.
type JobBlock struct {
// Don't include the standard Gorm UpdatedAt or DeletedAt fields, as they're useless here.
// Entries will never be updated, and should never be soft-deleted but just purged from existence.
ID uint
CreatedAt time.Time
JobID uint `gorm:"default:0;uniqueIndex:job_worker_tasktype"`
Job *Job `gorm:"foreignkey:JobID;references:ID;constraint:OnDelete:CASCADE"`
WorkerID uint `gorm:"default:0;uniqueIndex:job_worker_tasktype"`
Worker *Worker `gorm:"foreignkey:WorkerID;references:ID;constraint:OnDelete:CASCADE"`
TaskType string `gorm:"uniqueIndex:job_worker_tasktype"`
}
// AddWorkerToJobBlocklist prevents this Worker of getting any task, of this type, on this job, from the task scheduler.
func (db *DB) AddWorkerToJobBlocklist(ctx context.Context, job *Job, worker *Worker, taskType string) error {
if job.ID == 0 {
panic("Cannot add worker to job blocklist with zero job ID")
}
if worker.ID == 0 {
panic("Cannot add worker to job blocklist with zero worker ID")
}
if taskType == "" {
panic("Cannot add worker to job blocklist with empty task type")
}
queries := db.queries()
return queries.AddWorkerToJobBlocklist(ctx, sqlc.AddWorkerToJobBlocklistParams{
CreatedAt: db.nowNullable().Time,
JobID: int64(job.ID),
WorkerID: int64(worker.ID),
TaskType: taskType,
})
}
// FetchJobBlocklist fetches the blocklist for the given job.
// Workers are fetched too, and embedded in the returned list.
func (db *DB) FetchJobBlocklist(ctx context.Context, jobUUID string) ([]JobBlock, error) {
queries := db.queries()
rows, err := queries.FetchJobBlocklist(ctx, jobUUID)
if err != nil {
return nil, err
}
entries := make([]JobBlock, len(rows))
for idx, row := range rows {
entries[idx].ID = uint(row.JobBlock.ID)
entries[idx].CreatedAt = row.JobBlock.CreatedAt
entries[idx].TaskType = row.JobBlock.TaskType
entries[idx].JobID = uint(row.JobBlock.JobID)
entries[idx].WorkerID = uint(row.JobBlock.WorkerID)
entries[idx].Worker = convertSqlcWorker(row.Worker)
}
return entries, nil
}
// ClearJobBlocklist removes the entire blocklist of this job.
func (db *DB) ClearJobBlocklist(ctx context.Context, job *Job) error {
queries := db.queries()
return queries.ClearJobBlocklist(ctx, job.UUID)
}
func (db *DB) RemoveFromJobBlocklist(ctx context.Context, jobUUID, workerUUID, taskType string) error {
queries := db.queries()
return queries.RemoveFromJobBlocklist(ctx, sqlc.RemoveFromJobBlocklistParams{
JobUUID: jobUUID,
WorkerUUID: workerUUID,
TaskType: taskType,
})
}
// WorkersLeftToRun returns a set of worker UUIDs that can run tasks of the given type on the given job.
//
// NOTE: this does NOT consider the task failure list, which blocks individual
// workers from individual tasks. This is ONLY concerning the job blocklist.
func (db *DB) WorkersLeftToRun(ctx context.Context, job *Job, taskType string) (map[string]bool, error) {
queries := db.queries()
var (
workerUUIDs []string
err error
)
if job.WorkerTagID == nil {
workerUUIDs, err = queries.WorkersLeftToRun(ctx, sqlc.WorkersLeftToRunParams{
JobID: int64(job.ID),
TaskType: taskType,
})
} else {
workerUUIDs, err = queries.WorkersLeftToRunWithWorkerTag(ctx,
sqlc.WorkersLeftToRunWithWorkerTagParams{
JobID: int64(job.ID),
TaskType: taskType,
WorkerTagID: int64(*job.WorkerTagID),
})
}
if err != nil {
return nil, err
}
// Construct a map of UUIDs.
uuidMap := map[string]bool{}
for _, uuid := range workerUUIDs {
uuidMap[uuid] = true
}
return uuidMap, nil
}
// CountTaskFailuresOfWorker returns the number of task failures of this worker, on this particular job and task type.
func (db *DB) CountTaskFailuresOfWorker(ctx context.Context, job *Job, worker *Worker, taskType string) (int, error) {
var numFailures int64
queries := db.queries()
numFailures, err := queries.CountTaskFailuresOfWorker(ctx, sqlc.CountTaskFailuresOfWorkerParams{
JobID: int64(job.ID),
WorkerID: int64(worker.ID),
TaskType: taskType,
})
if numFailures > math.MaxInt {
panic("overflow error in number of failures")
}
return int(numFailures), err
}