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:
Sybren A. Stüvel 2022-02-24 12:34:39 +01:00
parent 64db518f15
commit df77d09aa6
8 changed files with 447 additions and 30 deletions

View File

@ -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

View File

@ -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)

View File

@ -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)
}

View 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)
}

View 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))
}

View File

@ -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

View File

@ -47,33 +47,33 @@ var swaggerSpec = []string{
"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",
}
// GetSwagger returns the content of the embedded swagger specification file

View File

@ -76,8 +76,6 @@ const (
TaskStatusCompleted TaskStatus = "completed"
TaskStatusFailRequested TaskStatus = "fail-requested"
TaskStatusFailed TaskStatus = "failed"
TaskStatusPaused TaskStatus = "paused"