github.com/matrixorigin/matrixone@v1.2.0/pkg/hakeeper/operator/status_tracker.go (about) 1 // Copyright 2020 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 // Portions of this file are additionally subject to the following 15 // copyright. 16 // 17 // Copyright (C) 2021 Matrix Origin. 18 // 19 // Modified the behavior of the status tracker. 20 21 package operator 22 23 import ( 24 "sync" 25 "time" 26 ) 27 28 // Only record non-end status and one end status. 29 type statusTimes [firstEndStatus + 1]time.Time 30 31 // OpStatusTracker represents the status of an operator. 32 type OpStatusTracker struct { 33 rw sync.RWMutex 34 current OpStatus // Current status 35 reachTimes statusTimes // Time when reach the current status 36 } 37 38 // NewOpStatusTracker creates an OpStatus. 39 func NewOpStatusTracker() OpStatusTracker { 40 return OpStatusTracker{ 41 current: STARTED, 42 reachTimes: statusTimes{STARTED: time.Now()}, 43 } 44 } 45 46 // Status returns current status. 47 func (trk *OpStatusTracker) Status() OpStatus { 48 trk.rw.RLock() 49 defer trk.rw.RUnlock() 50 return trk.current 51 } 52 53 // SetStatus only used for tests. 54 func (trk *OpStatusTracker) setStatus(status OpStatus) { 55 trk.rw.Lock() 56 defer trk.rw.Unlock() 57 trk.current = status 58 } 59 60 // ReachTime returns the reach time of current status. 61 func (trk *OpStatusTracker) ReachTime() time.Time { 62 trk.rw.RLock() 63 defer trk.rw.RUnlock() 64 return trk.getTime(trk.current) 65 } 66 67 // ReachTimeOf returns the time when reached given status. If didn't reached the given status, return zero. 68 func (trk *OpStatusTracker) ReachTimeOf(s OpStatus) time.Time { 69 trk.rw.RLock() 70 defer trk.rw.RUnlock() 71 return trk.getTime(s) 72 } 73 74 func (trk *OpStatusTracker) getTime(s OpStatus) time.Time { 75 if s < firstEndStatus { 76 return trk.reachTimes[s] 77 } else if trk.current == s { 78 return trk.reachTimes[firstEndStatus] 79 } else { 80 return time.Time{} 81 } 82 } 83 84 // To transfer the current status to dst if this transition is valid, 85 // returns whether transferred. 86 func (trk *OpStatusTracker) To(dst OpStatus) bool { 87 trk.rw.Lock() 88 defer trk.rw.Unlock() 89 return trk.toLocked(dst) 90 } 91 92 func (trk *OpStatusTracker) toLocked(dst OpStatus) bool { 93 if dst < statusCount && validTrans[trk.current][dst] { 94 trk.current = dst 95 trk.setTime(trk.current, time.Now()) 96 return true 97 } 98 return false 99 } 100 101 func (trk *OpStatusTracker) setTime(st OpStatus, t time.Time) { 102 if st < firstEndStatus { 103 trk.reachTimes[st] = t 104 } else { 105 trk.reachTimes[firstEndStatus] = t 106 } 107 } 108 109 // IsEnd checks whether the current status is an end status. 110 func (trk *OpStatusTracker) IsEnd() bool { 111 trk.rw.RLock() 112 defer trk.rw.RUnlock() 113 return IsEndStatus(trk.current) 114 } 115 116 // CheckExpired checks if expired, and update the current status. 117 func (trk *OpStatusTracker) CheckExpired(exp time.Duration) bool { 118 trk.rw.Lock() 119 defer trk.rw.Unlock() 120 switch trk.current { 121 case STARTED: 122 if time.Since(trk.reachTimes[STARTED]) < exp { 123 return false 124 } 125 _ = trk.toLocked(EXPIRED) 126 return true 127 } 128 return trk.current == EXPIRED 129 } 130 131 // String implements fmt.Stringer. 132 func (trk *OpStatusTracker) String() string { 133 trk.rw.RLock() 134 defer trk.rw.RUnlock() 135 return OpStatusToString(trk.current) 136 }