github.com/matrixorigin/matrixone@v0.7.0/pkg/vm/engine/tae/tasks/worker/worker.go (about) 1 // Copyright 2021 Matrix Origin 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 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package ops 16 17 import ( 18 "fmt" 19 "sync/atomic" 20 21 "github.com/matrixorigin/matrixone/pkg/common/moerr" 22 "github.com/matrixorigin/matrixone/pkg/logutil" 23 "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/common" 24 iops "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/tasks/ops/base" 25 iw "github.com/matrixorigin/matrixone/pkg/vm/engine/tae/tasks/worker/base" 26 ) 27 28 type Cmd = uint8 29 30 const ( 31 QUIT Cmd = iota 32 ) 33 34 const ( 35 CREATED int32 = iota 36 RUNNING 37 StoppingReceiver 38 StoppingCMD 39 STOPPED 40 ) 41 42 const ( 43 QueueSize = 10000 44 ) 45 46 var ( 47 _ iw.IOpWorker = (*OpWorker)(nil) 48 ) 49 50 type OpExecFunc func(op iops.IOp) 51 52 type Stats struct { 53 Processed atomic.Uint64 54 Successed atomic.Uint64 55 Failed atomic.Uint64 56 AvgTime atomic.Int64 57 } 58 59 func (s *Stats) AddProcessed() { 60 s.Processed.Add(1) 61 } 62 63 func (s *Stats) AddSuccessed() { 64 s.Successed.Add(1) 65 } 66 67 func (s *Stats) AddFailed() { 68 s.Failed.Add(1) 69 } 70 71 func (s *Stats) RecordTime(t int64) { 72 procced := s.Processed.Load() 73 avg := s.AvgTime.Load() 74 //TODO: avgTime is wrong 75 s.AvgTime.Store((avg*int64(procced-1) + t) / int64(procced)) 76 } 77 78 func (s *Stats) String() string { 79 r := fmt.Sprintf("Total: %d, Succ: %d, Fail: %d, AvgTime: %dus", 80 s.Processed.Load(), 81 s.Failed.Load(), 82 s.AvgTime.Load(), 83 s.AvgTime.Load()) 84 return r 85 } 86 87 type OpWorker struct { 88 Name string 89 OpC chan iops.IOp 90 CmdC chan Cmd 91 State atomic.Int32 92 Pending atomic.Int64 93 ClosedCh chan struct{} 94 Stats Stats 95 ExecFunc OpExecFunc 96 CancelFunc OpExecFunc 97 } 98 99 func NewOpWorker(name string, args ...int) *OpWorker { 100 var l int 101 if len(args) == 0 { 102 l = QueueSize 103 } else { 104 l = args[0] 105 if l < 0 { 106 logutil.Warnf("Create OpWorker with negtive queue size %d", l) 107 l = QueueSize 108 } 109 } 110 if name == "" { 111 name = fmt.Sprintf("[worker-%d]", common.NextGlobalSeqNum()) 112 } 113 worker := &OpWorker{ 114 Name: name, 115 OpC: make(chan iops.IOp, l), 116 CmdC: make(chan Cmd, l), 117 ClosedCh: make(chan struct{}), 118 } 119 worker.State.Store(CREATED) 120 worker.ExecFunc = worker.onOp 121 worker.CancelFunc = worker.opCancelOp 122 return worker 123 } 124 125 func (w *OpWorker) Start() { 126 logutil.Debugf("%s Started", w.Name) 127 if w.State.Load() != CREATED { 128 panic(fmt.Sprintf("logic error: %v", w.State.Load())) 129 } 130 w.State.Store(RUNNING) 131 go func() { 132 for { 133 state := w.State.Load() 134 if state == STOPPED { 135 break 136 } 137 select { 138 case op := <-w.OpC: 139 w.ExecFunc(op) 140 // if state == RUNNING { 141 // w.ExecFunc(op) 142 // } else { 143 // w.CancelFunc(op) 144 // } 145 w.Pending.Add(-1) 146 case cmd := <-w.CmdC: 147 w.onCmd(cmd) 148 } 149 } 150 }() 151 } 152 153 func (w *OpWorker) Stop() { 154 w.StopReceiver() 155 w.WaitStop() 156 logutil.Debugf("%s Stopped", w.Name) 157 } 158 159 func (w *OpWorker) StopReceiver() { 160 state := w.State.Load() 161 if state >= StoppingReceiver { 162 return 163 } 164 w.State.CompareAndSwap(state, StoppingReceiver) 165 } 166 167 func (w *OpWorker) WaitStop() { 168 state := w.State.Load() 169 if state <= RUNNING { 170 panic("logic error") 171 } 172 if state == STOPPED { 173 return 174 } 175 if w.State.CompareAndSwap(StoppingReceiver, StoppingCMD) { 176 pending := w.Pending.Load() 177 for { 178 if pending == 0 { 179 break 180 } 181 pending = w.Pending.Load() 182 } 183 w.CmdC <- QUIT 184 } 185 <-w.ClosedCh 186 } 187 188 func (w *OpWorker) SendOp(op iops.IOp) bool { 189 state := w.State.Load() 190 if state != RUNNING { 191 return false 192 } 193 w.Pending.Add(1) 194 if w.State.Load() != RUNNING { 195 w.Pending.Add(-1) 196 return false 197 } 198 w.OpC <- op 199 return true 200 } 201 202 func (w *OpWorker) opCancelOp(op iops.IOp) { 203 op.SetError(moerr.NewInternalErrorNoCtx("op cancelled")) 204 } 205 206 func (w *OpWorker) onOp(op iops.IOp) { 207 err := op.OnExec() 208 w.Stats.AddProcessed() 209 if err != nil { 210 w.Stats.AddFailed() 211 } else { 212 w.Stats.AddSuccessed() 213 } 214 op.SetError(err) 215 w.Stats.RecordTime(op.GetExecutTime()) 216 } 217 218 func (w *OpWorker) onCmd(cmd Cmd) { 219 switch cmd { 220 case QUIT: 221 // log.Infof("Quit OpWorker") 222 close(w.CmdC) 223 close(w.OpC) 224 if !w.State.CompareAndSwap(StoppingCMD, STOPPED) { 225 panic("logic error") 226 } 227 w.ClosedCh <- struct{}{} 228 default: 229 panic(fmt.Sprintf("Unsupported cmd %d", cmd)) 230 } 231 } 232 233 func (w *OpWorker) StatsString() string { 234 s := fmt.Sprintf("| Stats | %s | w | %s", w.Stats.String(), w.Name) 235 return s 236 }