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