github.com/pawelgaczynski/gain@v0.4.0-alpha.0.20230821120126-41f1e60a18da/worker.go (about)

     1  // Copyright (c) 2023 Paweł Gaczyński
     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 gain
    16  
    17  import (
    18  	"syscall"
    19  
    20  	"github.com/pawelgaczynski/giouring"
    21  	"github.com/rs/zerolog"
    22  	"golang.org/x/sys/unix"
    23  )
    24  
    25  type startSignal int
    26  
    27  const (
    28  	done startSignal = iota
    29  )
    30  
    31  type worker interface {
    32  	loop(socket int) error
    33  	setIndex(index int)
    34  	index() int
    35  	shutdown()
    36  	ringFd() int
    37  	started() bool
    38  	close()
    39  }
    40  
    41  type workerStartListener func()
    42  
    43  type workerConfig struct {
    44  	cpuAffinity     bool
    45  	processPriority bool
    46  	maxSQEntries    uint
    47  	maxCQEvents     int
    48  	loggerLevel     zerolog.Level
    49  	prettyLogger    bool
    50  }
    51  
    52  type workerImpl struct {
    53  	*looper
    54  	*connCloser
    55  	*shutdowner
    56  	idx            int
    57  	logger         zerolog.Logger
    58  	startedChan    chan startSignal
    59  	onCloseHandler func()
    60  }
    61  
    62  func (w *workerImpl) ringFd() int {
    63  	return w.looper.ring.RingFd()
    64  }
    65  
    66  func (w *workerImpl) setIndex(index int) {
    67  	w.idx = index
    68  }
    69  
    70  func (w *workerImpl) index() int {
    71  	return w.idx
    72  }
    73  
    74  func (w *workerImpl) processEvent(cqe *giouring.CompletionQueueEvent,
    75  	skipErrorChecker func(*giouring.CompletionQueueEvent) bool,
    76  ) bool {
    77  	w.logDebug().
    78  		Int32("Res", cqe.Res).
    79  		Uint64("req key", cqe.UserData & ^allFlagsMask).
    80  		Uint64("user data", cqe.UserData).
    81  		Str("req flag", flagToString(cqe.UserData)).
    82  		Msg("Process event")
    83  
    84  	switch {
    85  	case cqe.Res < 0:
    86  		if !skipErrorChecker(cqe) {
    87  			w.logError(nil).
    88  				Str("error", unix.ErrnoName(-syscall.Errno(cqe.Res))).
    89  				Str("req flag", flagToString(cqe.UserData)).
    90  				Uint64("req key", cqe.UserData & ^allFlagsMask).
    91  				Uint64("user data", cqe.UserData).
    92  				Msg("worker request returns error code")
    93  		}
    94  
    95  		return true
    96  
    97  	case cqe.UserData == 0:
    98  		w.logError(nil).
    99  			Msg("user data flag is missing")
   100  
   101  		return true
   102  	}
   103  
   104  	return false
   105  }
   106  
   107  func (w *workerImpl) logDebug() *zerolog.Event {
   108  	return w.logger.Debug().Int("worker index", w.index()).Int("ring fd", w.ringFd())
   109  }
   110  
   111  func (w *workerImpl) logInfo() *zerolog.Event {
   112  	return w.logger.Info().Int("worker index", w.index()).Int("ring fd", w.ringFd())
   113  }
   114  
   115  func (w *workerImpl) logWarn() *zerolog.Event {
   116  	return w.logger.Warn().Int("worker index", w.index()).Int("ring fd", w.ringFd())
   117  }
   118  
   119  func (w *workerImpl) logError(err error) *zerolog.Event {
   120  	return w.logger.Error().Int("worker index", w.index()).Int("ring fd", w.ringFd()).Err(err)
   121  }
   122  
   123  func (w *workerImpl) close() {
   124  	if w.looper.ring != nil {
   125  		w.looper.ring.QueueExit()
   126  	}
   127  }
   128  
   129  func newWorkerImpl(
   130  	ring *giouring.Ring, config workerConfig, index int, logger zerolog.Logger,
   131  ) *workerImpl {
   132  	return &workerImpl{
   133  		logger:      logger,
   134  		shutdowner:  newShutdowner(),
   135  		connCloser:  newConnCloser(ring, logger),
   136  		looper:      newLooper(ring, config.cpuAffinity, config.processPriority, config.maxCQEvents),
   137  		startedChan: make(chan startSignal),
   138  		idx:         index,
   139  	}
   140  }