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  }