From df77d09aa6ae13a283a085e12c6772d47cb861c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Thu, 24 Feb 2022 12:34:39 +0100 Subject: [PATCH] Start of a task/job state machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- FEATURES.md | 1 + internal/manager/api_impl/api_impl.go | 6 + .../mocks/interfaces_mock.gen.go | 96 ++++++++ .../task_state_machine/task_state_machine.go | 210 ++++++++++++++++++ .../task_state_machine_test.go | 106 +++++++++ pkg/api/flamenco-manager.yaml | 2 +- pkg/api/openapi_spec.gen.go | 54 ++--- pkg/api/openapi_types.gen.go | 2 - 8 files changed, 447 insertions(+), 30 deletions(-) create mode 100644 internal/manager/task_state_machine/mocks/interfaces_mock.gen.go create mode 100644 internal/manager/task_state_machine/task_state_machine.go create mode 100644 internal/manager/task_state_machine/task_state_machine_test.go diff --git a/FEATURES.md b/FEATURES.md index 2b3b51e6..a3dc8f71 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -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 diff --git a/internal/manager/api_impl/api_impl.go b/internal/manager/api_impl/api_impl.go index a8969491..7cb99777 100644 --- a/internal/manager/api_impl/api_impl.go +++ b/internal/manager/api_impl/api_impl.go @@ -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) diff --git a/internal/manager/task_state_machine/mocks/interfaces_mock.gen.go b/internal/manager/task_state_machine/mocks/interfaces_mock.gen.go new file mode 100644 index 00000000..94eca89d --- /dev/null +++ b/internal/manager/task_state_machine/mocks/interfaces_mock.gen.go @@ -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) +} diff --git a/internal/manager/task_state_machine/task_state_machine.go b/internal/manager/task_state_machine/task_state_machine.go new file mode 100644 index 00000000..1e028cce --- /dev/null +++ b/internal/manager/task_state_machine/task_state_machine.go @@ -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 . + * + * ***** 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) +} diff --git a/internal/manager/task_state_machine/task_state_machine_test.go b/internal/manager/task_state_machine/task_state_machine_test.go new file mode 100644 index 00000000..79a8b32c --- /dev/null +++ b/internal/manager/task_state_machine/task_state_machine_test.go @@ -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 . + * + * ***** 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)) +} diff --git a/pkg/api/flamenco-manager.yaml b/pkg/api/flamenco-manager.yaml index 5e964b85..2bfb7d10 100644 --- a/pkg/api/flamenco-manager.yaml +++ b/pkg/api/flamenco-manager.yaml @@ -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 diff --git a/pkg/api/openapi_spec.gen.go b/pkg/api/openapi_spec.gen.go index 36a2999b..c77b810b 100644 --- a/pkg/api/openapi_spec.gen.go +++ b/pkg/api/openapi_spec.gen.go @@ -47,33 +47,33 @@ var swaggerSpec = []string{ "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", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/pkg/api/openapi_types.gen.go b/pkg/api/openapi_types.gen.go index 8fb4843a..da42dd62 100644 --- a/pkg/api/openapi_types.gen.go +++ b/pkg/api/openapi_types.gen.go @@ -76,8 +76,6 @@ const ( TaskStatusCompleted TaskStatus = "completed" - TaskStatusFailRequested TaskStatus = "fail-requested" - TaskStatusFailed TaskStatus = "failed" TaskStatusPaused TaskStatus = "paused"