Start of a task/job state machine
The task status change → job status change code is a direct port of the Flamenco Server v2 code written in Python. There is no job status change → task status changes logic yet, and the tests are also far from complete.
This commit is contained in:
parent
64db518f15
commit
df77d09aa6
@ -11,6 +11,7 @@ rediscovered.
|
||||
- [x] Task Scheduler
|
||||
- [x] Unify command definition (type/parameters in persistence layer, name/settings in API layer)
|
||||
- [ ] Job status change handling
|
||||
- [ ] Port the old 'fail-requested' task status handling code to the new Manager
|
||||
- [ ] Task timeout monitoring
|
||||
- [ ] Worker Blacklisting & failed task requeueing
|
||||
- [ ] Worker timeout monitoring
|
||||
|
@ -59,6 +59,12 @@ type PersistenceService interface {
|
||||
ScheduleTask(w *persistence.Worker) (*persistence.Task, error)
|
||||
}
|
||||
|
||||
// TaskStateMachine interfaces task_state_machine.StateMachine.
|
||||
type TaskStateMachine interface {
|
||||
IsTaskStatusChangeValid(task *persistence.Task, newStatus api.TaskStatus) bool
|
||||
TaskStatusChange(ctx context.Context, task *persistence.Task, newStatus api.TaskStatus) error
|
||||
}
|
||||
|
||||
type JobCompiler interface {
|
||||
ListJobTypes() api.AvailableJobTypes
|
||||
Compile(ctx context.Context, job api.SubmittedJob) (*job_compilers.AuthoredJob, error)
|
||||
|
@ -0,0 +1,96 @@
|
||||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: gitlab.com/blender/flamenco-ng-poc/internal/manager/task_state_machine (interfaces: PersistenceService)
|
||||
|
||||
// Package mocks is a generated GoMock package.
|
||||
package mocks
|
||||
|
||||
import (
|
||||
context "context"
|
||||
reflect "reflect"
|
||||
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
persistence "gitlab.com/blender/flamenco-ng-poc/internal/manager/persistence"
|
||||
api "gitlab.com/blender/flamenco-ng-poc/pkg/api"
|
||||
)
|
||||
|
||||
// MockPersistenceService is a mock of PersistenceService interface.
|
||||
type MockPersistenceService struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockPersistenceServiceMockRecorder
|
||||
}
|
||||
|
||||
// MockPersistenceServiceMockRecorder is the mock recorder for MockPersistenceService.
|
||||
type MockPersistenceServiceMockRecorder struct {
|
||||
mock *MockPersistenceService
|
||||
}
|
||||
|
||||
// NewMockPersistenceService creates a new mock instance.
|
||||
func NewMockPersistenceService(ctrl *gomock.Controller) *MockPersistenceService {
|
||||
mock := &MockPersistenceService{ctrl: ctrl}
|
||||
mock.recorder = &MockPersistenceServiceMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||
func (m *MockPersistenceService) EXPECT() *MockPersistenceServiceMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// CountTasksOfJobInStatus mocks base method.
|
||||
func (m *MockPersistenceService) CountTasksOfJobInStatus(arg0 context.Context, arg1 *persistence.Job, arg2 api.TaskStatus) (int, int, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "CountTasksOfJobInStatus", arg0, arg1, arg2)
|
||||
ret0, _ := ret[0].(int)
|
||||
ret1, _ := ret[1].(int)
|
||||
ret2, _ := ret[2].(error)
|
||||
return ret0, ret1, ret2
|
||||
}
|
||||
|
||||
// CountTasksOfJobInStatus indicates an expected call of CountTasksOfJobInStatus.
|
||||
func (mr *MockPersistenceServiceMockRecorder) CountTasksOfJobInStatus(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CountTasksOfJobInStatus", reflect.TypeOf((*MockPersistenceService)(nil).CountTasksOfJobInStatus), arg0, arg1, arg2)
|
||||
}
|
||||
|
||||
// JobHasTasksInStatus mocks base method.
|
||||
func (m *MockPersistenceService) JobHasTasksInStatus(arg0 context.Context, arg1 *persistence.Job, arg2 api.TaskStatus) (bool, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "JobHasTasksInStatus", arg0, arg1, arg2)
|
||||
ret0, _ := ret[0].(bool)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// JobHasTasksInStatus indicates an expected call of JobHasTasksInStatus.
|
||||
func (mr *MockPersistenceServiceMockRecorder) JobHasTasksInStatus(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "JobHasTasksInStatus", reflect.TypeOf((*MockPersistenceService)(nil).JobHasTasksInStatus), arg0, arg1, arg2)
|
||||
}
|
||||
|
||||
// SaveJobStatus mocks base method.
|
||||
func (m *MockPersistenceService) SaveJobStatus(arg0 context.Context, arg1 *persistence.Job) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "SaveJobStatus", arg0, arg1)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// SaveJobStatus indicates an expected call of SaveJobStatus.
|
||||
func (mr *MockPersistenceServiceMockRecorder) SaveJobStatus(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveJobStatus", reflect.TypeOf((*MockPersistenceService)(nil).SaveJobStatus), arg0, arg1)
|
||||
}
|
||||
|
||||
// SaveTask mocks base method.
|
||||
func (m *MockPersistenceService) SaveTask(arg0 context.Context, arg1 *persistence.Task) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "SaveTask", arg0, arg1)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// SaveTask indicates an expected call of SaveTask.
|
||||
func (mr *MockPersistenceServiceMockRecorder) SaveTask(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SaveTask", reflect.TypeOf((*MockPersistenceService)(nil).SaveTask), arg0, arg1)
|
||||
}
|
210
internal/manager/task_state_machine/task_state_machine.go
Normal file
210
internal/manager/task_state_machine/task_state_machine.go
Normal file
@ -0,0 +1,210 @@
|
||||
package task_state_machine
|
||||
|
||||
/* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* Original Code Copyright (C) 2022 Blender Foundation.
|
||||
*
|
||||
* This file is part of Flamenco.
|
||||
*
|
||||
* Flamenco is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* Flamenco is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* Flamenco. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK ***** */
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/rs/zerolog/log"
|
||||
"gitlab.com/blender/flamenco-ng-poc/internal/manager/persistence"
|
||||
"gitlab.com/blender/flamenco-ng-poc/pkg/api"
|
||||
)
|
||||
|
||||
// taskFailJobPercentage is the percentage of a job's tasks that need to fail to
|
||||
// trigger failure of the entire job.
|
||||
const taskFailJobPercentage = 10 // Integer from 0 to 100.
|
||||
|
||||
// StateMachine handles task and job status changes.
|
||||
type StateMachine struct {
|
||||
persist PersistenceService
|
||||
}
|
||||
|
||||
// Generate mock implementations of these interfaces.
|
||||
//go:generate go run github.com/golang/mock/mockgen -destination mocks/interfaces_mock.gen.go -package mocks gitlab.com/blender/flamenco-ng-poc/internal/manager/task_state_machine PersistenceService
|
||||
|
||||
type PersistenceService interface { // Subset of persistence.DB
|
||||
SaveTask(ctx context.Context, task *persistence.Task) error
|
||||
SaveJobStatus(ctx context.Context, j *persistence.Job) error
|
||||
|
||||
JobHasTasksInStatus(ctx context.Context, job *persistence.Job, taskStatus api.TaskStatus) (bool, error)
|
||||
CountTasksOfJobInStatus(ctx context.Context, job *persistence.Job, taskStatus api.TaskStatus) (numInStatus, numTotal int, err error)
|
||||
}
|
||||
|
||||
func NewStateMachine(persist PersistenceService) *StateMachine {
|
||||
return &StateMachine{
|
||||
persist: persist,
|
||||
}
|
||||
}
|
||||
|
||||
// TaskStatusChange updates the task's status to the new one.
|
||||
// `task` is expected to still have its original status, and have a filled `Job` pointer.
|
||||
func (sm *StateMachine) TaskStatusChange(ctx context.Context, task *persistence.Task, newTaskStatus api.TaskStatus) error {
|
||||
job := task.Job
|
||||
if job == nil {
|
||||
log.Panic().Str("task", task.UUID).Msg("task without job, cannot handle this")
|
||||
return nil // Will not run because of the panic.
|
||||
}
|
||||
|
||||
logger := log.With().
|
||||
Str("task", task.UUID).
|
||||
Str("job", job.UUID).
|
||||
Str("taskStatusOld", string(task.Status)).
|
||||
Str("taskStatusNew", string(newTaskStatus)).
|
||||
Logger()
|
||||
logger.Debug().Msg("task state changed")
|
||||
|
||||
task.Status = newTaskStatus
|
||||
if err := sm.persist.SaveTask(ctx, task); err != nil {
|
||||
return fmt.Errorf("error saving task to database: %w", err)
|
||||
}
|
||||
if err := sm.updateJobAfterTaskStatusChange(ctx, task, newTaskStatus); err != nil {
|
||||
return fmt.Errorf("error updating job after task status change: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// updateJobAfterTaskStatusChange updates the job status based on the status of
|
||||
// this task and other tasks in the job.
|
||||
func (sm *StateMachine) updateJobAfterTaskStatusChange(
|
||||
ctx context.Context, task *persistence.Task, newTaskStatus api.TaskStatus,
|
||||
) error {
|
||||
|
||||
job := task.Job
|
||||
|
||||
logger := log.With().
|
||||
Str("job", job.UUID).
|
||||
Str("task", task.UUID).
|
||||
Str("taskStatusOld", string(task.Status)).
|
||||
Str("taskStatusNew", string(newTaskStatus)).
|
||||
Logger()
|
||||
|
||||
// If the job has status 'ifStatus', move it to status 'thenStatus'.
|
||||
jobStatusIfAThenB := func(ifStatus, thenStatus api.JobStatus) error {
|
||||
if job.Status != ifStatus {
|
||||
return nil
|
||||
}
|
||||
logger.Info().
|
||||
Str("jobStatusOld", string(ifStatus)).
|
||||
Str("jobStatusNew", string(thenStatus)).
|
||||
Msg("Job changed status because one of its task changed status")
|
||||
return sm.JobStatusChange(ctx, job, thenStatus)
|
||||
}
|
||||
|
||||
// Every 'case' in this switch MUST return. Just for sanity's sake.
|
||||
switch newTaskStatus {
|
||||
case api.TaskStatusQueued:
|
||||
// Re-queueing a task on a completed job should re-queue the job too.
|
||||
return jobStatusIfAThenB(api.JobStatusCompleted, api.JobStatusQueued)
|
||||
|
||||
case api.TaskStatusCancelRequested:
|
||||
// Requesting cancellation of a single task has no influence on the job itself.
|
||||
return nil
|
||||
|
||||
case api.TaskStatusPaused:
|
||||
// Pausing a task has no impact on the job.
|
||||
return nil
|
||||
|
||||
case api.TaskStatusCanceled:
|
||||
// Only trigger cancellation/failure of the job if that was actually requested.
|
||||
// A user can also cancel a single task from the web UI or API, in which
|
||||
// case the job should just keep running.
|
||||
if job.Status != api.JobStatusCancelRequested {
|
||||
return nil
|
||||
}
|
||||
// This could be the last 'cancel-requested' task to go to 'canceled'.
|
||||
hasCancelReq, err := sm.persist.JobHasTasksInStatus(ctx, job, api.TaskStatusCancelRequested)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !hasCancelReq {
|
||||
logger.Info().Msg("last task of job went from cancel-requested to canceled")
|
||||
return sm.JobStatusChange(ctx, job, api.JobStatusCanceled)
|
||||
}
|
||||
return nil
|
||||
|
||||
case api.TaskStatusFailed:
|
||||
// Count the number of failed tasks. If it is over the threshold, fail the job.
|
||||
numFailed, numTotal, err := sm.persist.CountTasksOfJobInStatus(ctx, job, api.TaskStatusFailed)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
failedPercentage := int(float64(numFailed) / float64(numTotal) * 100)
|
||||
failLogger := logger.With().
|
||||
Int("taskNumTotal", numTotal).
|
||||
Int("taskNumFailed", numFailed).
|
||||
Int("failedPercentage", failedPercentage).
|
||||
Int("threshold", taskFailJobPercentage).
|
||||
Logger()
|
||||
|
||||
if failedPercentage >= taskFailJobPercentage {
|
||||
failLogger.Info().Msg("failing job because too many of its tasks failed")
|
||||
return sm.JobStatusChange(ctx, job, api.JobStatusFailed)
|
||||
}
|
||||
// If the job didn't fail, this failure indicates that at least the job is active.
|
||||
failLogger.Info().Msg("task failed, but not enough to fail the job")
|
||||
return jobStatusIfAThenB(api.JobStatusQueued, api.JobStatusActive)
|
||||
|
||||
case api.TaskStatusActive, api.TaskStatusSoftFailed:
|
||||
switch job.Status {
|
||||
case api.JobStatusActive, api.JobStatusCancelRequested:
|
||||
// Do nothing, job is already in the desired status.
|
||||
return nil
|
||||
default:
|
||||
logger.Info().Msg("job became active because one of its task changed status")
|
||||
return sm.JobStatusChange(ctx, job, api.JobStatusActive)
|
||||
}
|
||||
|
||||
case api.TaskStatusCompleted:
|
||||
numComplete, numTotal, err := sm.persist.CountTasksOfJobInStatus(ctx, job, api.TaskStatusCompleted)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if numComplete == numTotal {
|
||||
logger.Info().Msg("all tasks of job are completed, job is completed")
|
||||
return sm.JobStatusChange(ctx, job, api.JobStatusCompleted)
|
||||
}
|
||||
logger.Info().
|
||||
Int("taskNumTotal", numTotal).
|
||||
Int("taskNumComplete", numComplete).
|
||||
Msg("task completed; there are more tasks to do")
|
||||
return jobStatusIfAThenB(api.JobStatusQueued, api.JobStatusActive)
|
||||
|
||||
default:
|
||||
logger.Warn().Msg("task obtained status that Flamenco did not expect")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (sm *StateMachine) JobStatusChange(ctx context.Context, job *persistence.Job, newJobStatus api.JobStatus) error {
|
||||
logger := log.With().
|
||||
Str("job", job.UUID).
|
||||
Str("jobStatusOld", string(job.Status)).
|
||||
Str("jobStatusNew", string(newJobStatus)).
|
||||
Logger()
|
||||
|
||||
logger.Info().Msg("job status changed")
|
||||
|
||||
// TODO: actually respond to status change, instead of just saving the new job state.
|
||||
|
||||
job.Status = newJobStatus
|
||||
return sm.persist.SaveJobStatus(ctx, job)
|
||||
}
|
106
internal/manager/task_state_machine/task_state_machine_test.go
Normal file
106
internal/manager/task_state_machine/task_state_machine_test.go
Normal file
@ -0,0 +1,106 @@
|
||||
package task_state_machine
|
||||
|
||||
/* ***** BEGIN GPL LICENSE BLOCK *****
|
||||
*
|
||||
* Original Code Copyright (C) 2022 Blender Foundation.
|
||||
*
|
||||
* This file is part of Flamenco.
|
||||
*
|
||||
* Flamenco is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later
|
||||
* version.
|
||||
*
|
||||
* Flamenco is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* Flamenco. If not, see <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
* ***** END GPL LICENSE BLOCK ***** */
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"gitlab.com/blender/flamenco-ng-poc/internal/manager/persistence"
|
||||
"gitlab.com/blender/flamenco-ng-poc/internal/manager/task_state_machine/mocks"
|
||||
"gitlab.com/blender/flamenco-ng-poc/pkg/api"
|
||||
)
|
||||
|
||||
type StateMachineMocks struct {
|
||||
persist *mocks.MockPersistenceService
|
||||
}
|
||||
|
||||
func mockedTaskStateMachine(mockCtrl *gomock.Controller) (*StateMachine, *StateMachineMocks) {
|
||||
mocks := StateMachineMocks{
|
||||
persist: mocks.NewMockPersistenceService(mockCtrl),
|
||||
}
|
||||
sm := NewStateMachine(mocks.persist)
|
||||
return sm, &mocks
|
||||
}
|
||||
|
||||
func (m *StateMachineMocks) expectSaveTaskWithStatus(
|
||||
t *testing.T,
|
||||
task *persistence.Task,
|
||||
expectTaskStatus api.TaskStatus,
|
||||
) {
|
||||
m.persist.EXPECT().
|
||||
SaveTask(gomock.Any(), task).
|
||||
DoAndReturn(func(ctx context.Context, savedTask *persistence.Task) error {
|
||||
assert.Equal(t, expectTaskStatus, savedTask.Status)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (m *StateMachineMocks) expectSaveJobWithStatus(
|
||||
t *testing.T,
|
||||
job *persistence.Job,
|
||||
expectJobStatus api.JobStatus,
|
||||
) {
|
||||
m.persist.EXPECT().
|
||||
SaveJobStatus(gomock.Any(), job).
|
||||
DoAndReturn(func(ctx context.Context, savedJob *persistence.Job) error {
|
||||
assert.Equal(t, expectJobStatus, savedJob.Status)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
/* taskWithStatus() creates a task of a certain status, with a job of a certain status. */
|
||||
func taskWithStatus(jobStatus api.JobStatus, taskStatus api.TaskStatus) *persistence.Task {
|
||||
job := persistence.Job{
|
||||
Model: gorm.Model{ID: 47},
|
||||
UUID: "test-job-f3f5-4cef-9cd7-e67eb28eaf3e",
|
||||
|
||||
Status: jobStatus,
|
||||
}
|
||||
task := persistence.Task{
|
||||
Model: gorm.Model{ID: 327},
|
||||
UUID: "testtask-f474-4e28-aeea-8cbaf2fc96a5",
|
||||
|
||||
JobID: job.ID,
|
||||
Job: &job,
|
||||
|
||||
Status: taskStatus,
|
||||
}
|
||||
|
||||
return &task
|
||||
}
|
||||
|
||||
func TestTaskStatusChange(t *testing.T) {
|
||||
mockCtrl := gomock.NewController(t)
|
||||
defer mockCtrl.Finish()
|
||||
ctx := context.Background()
|
||||
|
||||
sm, mocks := mockedTaskStateMachine(mockCtrl)
|
||||
|
||||
task := taskWithStatus(api.JobStatusQueued, api.TaskStatusQueued)
|
||||
mocks.expectSaveTaskWithStatus(t, task, api.TaskStatusActive)
|
||||
mocks.expectSaveJobWithStatus(t, task.Job, api.JobStatusActive)
|
||||
assert.NoError(t, sm.TaskStatusChange(ctx, task, api.TaskStatusActive))
|
||||
}
|
@ -337,7 +337,7 @@ components:
|
||||
|
||||
TaskStatus:
|
||||
type: string
|
||||
enum: [active, canceled, completed, failed, queued, soft-failed, cancel-requested, fail-requested, paused]
|
||||
enum: [active, canceled, completed, failed, queued, soft-failed, cancel-requested, paused]
|
||||
|
||||
Command:
|
||||
type: object
|
||||
|
@ -47,33 +47,33 @@ var swaggerSpec = []string{
|
||||
"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",
|
||||
}
|
||||
|
||||
// GetSwagger returns the content of the embedded swagger specification file
|
||||
|
@ -76,8 +76,6 @@ const (
|
||||
|
||||
TaskStatusCompleted TaskStatus = "completed"
|
||||
|
||||
TaskStatusFailRequested TaskStatus = "fail-requested"
|
||||
|
||||
TaskStatusFailed TaskStatus = "failed"
|
||||
|
||||
TaskStatusPaused TaskStatus = "paused"
|
||||
|
Loading…
x
Reference in New Issue
Block a user