github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/engine/servermaster/job_fsm_test.go (about) 1 // Copyright 2022 PingCAP, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package servermaster 15 16 import ( 17 "testing" 18 19 "github.com/pingcap/tiflow/engine/framework" 20 frameModel "github.com/pingcap/tiflow/engine/framework/model" 21 "github.com/stretchr/testify/require" 22 ) 23 24 func TestJobFsmStateTrans(t *testing.T) { 25 t.Parallel() 26 27 fsm := NewJobFsm() 28 29 id := "fsm-test-job-master-1" 30 job := &frameModel.MasterMeta{ 31 ID: id, 32 Config: []byte("simple config"), 33 } 34 35 createWorkerCount := 0 36 37 // Failover, job fsm loads tombstone job master 38 fsm.JobDispatched(job, true) 39 err := fsm.IterWaitAckJobs(func(job *frameModel.MasterMeta) (string, error) { 40 createWorkerCount++ 41 return id, nil 42 }) 43 require.NoError(t, err) 44 require.Equal(t, 1, createWorkerCount) 45 require.Len(t, fsm.waitAckJobs, 1) 46 47 // job that is not added from failover won't be processed 48 err = fsm.IterWaitAckJobs(func(job *frameModel.MasterMeta) (string, error) { 49 createWorkerCount++ 50 return id, nil 51 }) 52 require.NoError(t, err) 53 require.Equal(t, 1, createWorkerCount) 54 55 // OnWorkerOnline, WaitAck -> Online 56 err = fsm.JobOnline(&framework.MockHandle{ 57 WorkerID: id, 58 WorkerStatus: &frameModel.WorkerStatus{State: frameModel.WorkerStateNormal}, 59 ExecutorID: "executor-1", 60 }) 61 require.NoError(t, err) 62 require.Empty(t, fsm.waitAckJobs) 63 require.Len(t, fsm.onlineJobs, 1) 64 65 // OnWorkerOffline, Online -> Pending 66 fsm.JobOffline(&framework.MockHandle{ 67 WorkerID: id, 68 WorkerStatus: &frameModel.WorkerStatus{State: frameModel.WorkerStateNormal}, 69 IsTombstone: true, 70 }, true /* needFailover */) 71 require.Empty(t, fsm.onlineJobs) 72 require.Len(t, fsm.pendingJobs, 1) 73 74 // Tick, process pending jobs, Pending -> WaitAck 75 dispatchedJobs := make([]*frameModel.MasterMeta, 0) 76 err = fsm.IterPendingJobs(func(job *frameModel.MasterMeta) (string, error) { 77 dispatchedJobs = append(dispatchedJobs, job) 78 return id, nil 79 }) 80 require.NoError(t, err) 81 require.Empty(t, fsm.pendingJobs) 82 require.Len(t, fsm.waitAckJobs, 1) 83 84 // Dispatch job meets error, WaitAck -> Pending 85 err = fsm.JobDispatchFailed(&framework.MockHandle{ 86 WorkerID: id, 87 WorkerStatus: &frameModel.WorkerStatus{State: frameModel.WorkerStateNormal}, 88 IsTombstone: true, 89 }) 90 require.NoError(t, err) 91 require.Len(t, fsm.pendingJobs, 1) 92 require.Empty(t, fsm.waitAckJobs) 93 94 // Tick, Pending -> WaitAck 95 err = fsm.IterPendingJobs(func(job *frameModel.MasterMeta) (string, error) { 96 return id, nil 97 }) 98 require.NoError(t, err) 99 require.Len(t, fsm.waitAckJobs, 1) 100 // job finished 101 fsm.JobOffline(&framework.MockHandle{ 102 WorkerID: id, 103 WorkerStatus: &frameModel.WorkerStatus{State: frameModel.WorkerStateNormal}, 104 IsTombstone: true, 105 }, false /*needFailover*/) 106 require.Empty(t, fsm.waitAckJobs) 107 108 // offline invalid job, will do nothing 109 invalidWorker := &framework.MockHandle{ 110 WorkerID: id + "invalid", 111 WorkerStatus: &frameModel.WorkerStatus{State: frameModel.WorkerStateNormal}, 112 ExecutorID: "executor-1", 113 } 114 115 fsm.JobOffline(invalidWorker, true) 116 }