github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/engine/framework/internal/master/worker_entry.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 master 15 16 import ( 17 "fmt" 18 "sync" 19 "time" 20 21 "github.com/pingcap/log" 22 frameModel "github.com/pingcap/tiflow/engine/framework/model" 23 "github.com/pingcap/tiflow/engine/model" 24 "go.uber.org/atomic" 25 "go.uber.org/zap" 26 ) 27 28 // workerEntryState is the state of a worker 29 // internal to WorkerManager. It is NOT part of 30 // the public API of Dataflow Engine. 31 type workerEntryState int32 32 33 const ( 34 workerEntryWait = workerEntryState(iota + 1) 35 workerEntryCreated 36 workerEntryNormal 37 workerEntryOffline 38 workerEntryTombstone 39 ) 40 41 // The following is the state-transition diagram. 42 // Refer to ../doc/worker_entry_fsm.puml for a UML version. 43 // 44 // workerEntryCreated workerEntryWait 45 // │ │ │ │ 46 // │ │ │ │ 47 // │ heartbeat heartbeat │ 48 // │ │ │ │ 49 // │ │ │ │ 50 // │ └────► workerEntryNormal ◄───┘ │ 51 // │ │ │ 52 // │ │ │ 53 // timeout timeout timeout 54 // │ │ │ 55 // ▼ ▼ ▼ 56 // workerEntryOffline ─────────► workerEntryTombstone 57 // callback 58 59 // workerEntry records the state of a worker managed by 60 // WorkerManager. 61 type workerEntry struct { 62 id frameModel.WorkerID 63 executorID model.ExecutorID 64 65 mu sync.Mutex 66 expireAt time.Time 67 state workerEntryState 68 69 receivedFinish atomic.Bool 70 71 statusMu sync.RWMutex 72 status *frameModel.WorkerStatus 73 } 74 75 func newWorkerEntry( 76 id frameModel.WorkerID, 77 executorID model.ExecutorID, 78 expireAt time.Time, 79 state workerEntryState, 80 initWorkerStatus *frameModel.WorkerStatus, 81 ) *workerEntry { 82 return &workerEntry{ 83 id: id, 84 executorID: executorID, 85 expireAt: expireAt, 86 state: state, 87 status: initWorkerStatus, 88 } 89 } 90 91 func newWaitingWorkerEntry( 92 id frameModel.WorkerID, 93 lastStatus *frameModel.WorkerStatus, 94 ) *workerEntry { 95 return newWorkerEntry(id, "", time.Time{}, workerEntryWait, lastStatus) 96 } 97 98 // String implements fmt.Stringer, note the implementation is not thread safe 99 func (e *workerEntry) String() string { 100 return fmt.Sprintf("{worker-id:%s, executor-id:%s, state:%d}", 101 e.id, e.executorID, e.state) 102 } 103 104 func (e *workerEntry) State() workerEntryState { 105 e.mu.Lock() 106 defer e.mu.Unlock() 107 108 return e.state 109 } 110 111 func (e *workerEntry) MarkAsTombstone() { 112 e.mu.Lock() 113 defer e.mu.Unlock() 114 115 if e.state == workerEntryWait || e.state == workerEntryOffline || e.IsFinished() { 116 // Only workerEntryWait and workerEntryOffline are allowed 117 // to transition to workerEntryTombstone. 118 e.state = workerEntryTombstone 119 return 120 } 121 122 log.Panic("Unreachable", zap.Stringer("entry", e)) 123 } 124 125 func (e *workerEntry) IsTombstone() bool { 126 e.mu.Lock() 127 defer e.mu.Unlock() 128 129 return e.state == workerEntryTombstone 130 } 131 132 func (e *workerEntry) MarkAsOnline(executor model.ExecutorID, expireAt time.Time) { 133 e.mu.Lock() 134 defer e.mu.Unlock() 135 136 if e.state == workerEntryCreated || e.state == workerEntryWait { 137 e.state = workerEntryNormal 138 e.expireAt = expireAt 139 e.executorID = executor 140 return 141 } 142 143 log.Panic("Unreachable", zap.Stringer("entry", e)) 144 } 145 146 func (e *workerEntry) MarkAsOffline() { 147 e.mu.Lock() 148 defer e.mu.Unlock() 149 150 if e.state == workerEntryCreated || e.state == workerEntryNormal { 151 e.state = workerEntryOffline 152 return 153 } 154 155 log.Panic("Unreachable", zap.Stringer("entry", e)) 156 } 157 158 func (e *workerEntry) Status() *frameModel.WorkerStatus { 159 e.statusMu.RLock() 160 defer e.statusMu.RUnlock() 161 162 return e.status 163 } 164 165 func (e *workerEntry) UpdateStatus(status *frameModel.WorkerStatus) { 166 e.statusMu.Lock() 167 defer e.statusMu.Unlock() 168 169 e.status = status 170 } 171 172 func (e *workerEntry) SetExpireTime(expireAt time.Time) { 173 e.mu.Lock() 174 defer e.mu.Unlock() 175 176 e.expireAt = expireAt 177 } 178 179 func (e *workerEntry) ExpireTime() time.Time { 180 e.mu.Lock() 181 defer e.mu.Unlock() 182 183 return e.expireAt 184 } 185 186 func (e *workerEntry) SetFinished() { 187 e.receivedFinish.Store(true) 188 } 189 190 func (e *workerEntry) IsFinished() bool { 191 return e.receivedFinish.Load() 192 }