github.com/godevsig/adaptiveservice@v0.9.23/scalamsgq.go (about)

     1  package adaptiveservice
     2  
     3  import (
     4  	"runtime"
     5  	"sync"
     6  	"time"
     7  )
     8  
     9  type metaKnownMsg struct {
    10  	stream ContextStream
    11  	msg    KnownMessage
    12  }
    13  
    14  type msgQ struct {
    15  	sync.Mutex
    16  	wp                *workerPool
    17  	lg                Logger
    18  	residentWorkers   int
    19  	qSize             int
    20  	ingressHighChan   chan *metaKnownMsg
    21  	ingressNormalChan chan *metaKnownMsg
    22  	ingressLowChan    chan *metaKnownMsg
    23  	egressChan        chan *metaKnownMsg
    24  	changeConfirmed   bool
    25  	done              chan struct{}
    26  }
    27  
    28  func newMsgQ(residentWorkers, qSizePerCore int, lg Logger) *msgQ {
    29  	qSize := qSizePerCore * runtime.NumCPU()
    30  	mq := &msgQ{
    31  		wp:                newWorkerPool(),
    32  		lg:                lg,
    33  		residentWorkers:   residentWorkers,
    34  		qSize:             qSize,
    35  		ingressNormalChan: make(chan *metaKnownMsg, qSize),
    36  		done:              make(chan struct{}),
    37  	}
    38  	go mq.autoScaler()
    39  	lg.Debugf("msgq created with qSize %v", qSize)
    40  	return mq
    41  }
    42  
    43  func (mq *msgQ) close() {
    44  	close(mq.done)
    45  	mq.done = nil
    46  	mq.wp.close()
    47  	mq.lg.Debugf("msgq closed")
    48  }
    49  
    50  func (mq *msgQ) reorder() {
    51  	mq.lg.Debugf("msgq reorder started")
    52  	defer mq.lg.Debugf("msgq reorder stopped")
    53  	for {
    54  		var msgH, msgN, msgL *metaKnownMsg
    55  		select {
    56  		case <-mq.done:
    57  			return
    58  		case msgH = <-mq.ingressHighChan:
    59  		case msgN = <-mq.ingressNormalChan:
    60  		case msgL = <-mq.ingressLowChan:
    61  		}
    62  
    63  		lenL := len(mq.ingressLowChan)
    64  		lenN := len(mq.ingressNormalChan)
    65  		lenH := len(mq.ingressHighChan)
    66  		mq.lg.Debugf("msgq lenH: %d, lenN: %d, lenL: %d", lenH, lenN, lenL)
    67  
    68  		if msgH != nil {
    69  			mq.egressChan <- msgH
    70  		}
    71  		for lenH != 0 {
    72  			mq.egressChan <- <-mq.ingressHighChan
    73  			lenH--
    74  		}
    75  
    76  		if msgN != nil {
    77  			mq.egressChan <- msgN
    78  		}
    79  		for lenN != 0 {
    80  			mq.egressChan <- <-mq.ingressNormalChan
    81  			lenN--
    82  		}
    83  
    84  		if msgL != nil {
    85  			mq.egressChan <- msgL
    86  		}
    87  		for lenL != 0 {
    88  			mq.egressChan <- <-mq.ingressLowChan
    89  			lenL--
    90  		}
    91  	}
    92  }
    93  
    94  func (mq *msgQ) worker(done <-chan struct{}, st status) {
    95  	for {
    96  		select {
    97  		case <-done:
    98  			return
    99  		case mm := <-mq.getEgressChan():
   100  			st.working()
   101  			reply := mm.msg.Handle(mm.stream)
   102  			mq.lg.Debugf("message: %#v handled, reply: %#v", mm.msg, reply)
   103  			//mq.lg.Debugf("message: %T handled, reply: %T", mm.msg, reply)
   104  			if reply != nil {
   105  				mm.stream.Send(reply)
   106  			}
   107  			st.idle()
   108  		}
   109  	}
   110  }
   111  
   112  func (mq *msgQ) autoScaler() {
   113  	for i := 0; i < mq.residentWorkers; i++ {
   114  		mq.wp.addWorker(mq.worker)
   115  	}
   116  
   117  	for {
   118  		if mq.done == nil {
   119  			return
   120  		}
   121  		egressChan := mq.getEgressChan()
   122  		should := len(egressChan) + mq.residentWorkers
   123  		now := mq.wp.len() // idle works
   124  
   125  		k := 1
   126  		switch {
   127  		case should > now:
   128  			k = should - now
   129  			mq.wp.addWorker(mq.worker)
   130  			mq.lg.Debugf("msgq autoScaler: now %d -> should %d: worker added", now, should)
   131  		case should < now:
   132  			k = now - should
   133  			mq.wp.rmWorker()
   134  			mq.lg.Debugf("msgq autoScaler: now %d -> should %d: worker removed", now, should)
   135  		}
   136  		time.Sleep(time.Second / time.Duration(k))
   137  	}
   138  }
   139  
   140  func (mq *msgQ) putMetaMsg(mm *metaKnownMsg) {
   141  	initChan := func(ch *chan *metaKnownMsg) {
   142  		mq.Lock()
   143  		if *ch == nil {
   144  			*ch = make(chan *metaKnownMsg, mq.qSize)
   145  		}
   146  		if mq.egressChan == nil {
   147  			mq.egressChan = make(chan *metaKnownMsg, mq.qSize)
   148  			mq.lg.Debugf("msgq egressChan created")
   149  			go mq.reorder()
   150  		}
   151  		mq.Unlock()
   152  	}
   153  
   154  	switch mm.msg.(type) {
   155  	case HighPriorityMessage:
   156  		//mq.lg.Debugf("msgq high priority message received: %#v", mm.msg)
   157  		mq.lg.Debugf("msgq high priority message received: %T", mm.msg)
   158  		if mq.ingressHighChan == nil {
   159  			initChan(&mq.ingressHighChan)
   160  			mq.lg.Debugf("msgq ingress high priority chan initialized")
   161  		}
   162  		mq.ingressHighChan <- mm
   163  	case LowPriorityMessage:
   164  		//mq.lg.Debugf("msgq low priority message received: %#v", mm.msg)
   165  		mq.lg.Debugf("msgq low priority message received: %T", mm.msg)
   166  		if mq.ingressLowChan == nil {
   167  			initChan(&mq.ingressLowChan)
   168  			mq.lg.Debugf("msgq ingress low priority chan initialized")
   169  		}
   170  		mq.ingressLowChan <- mm
   171  	default:
   172  		//mq.lg.Debugf("msgq normal priority message received: %#v", mm.msg)
   173  		mq.lg.Debugf("msgq normal priority message received: %T", mm.msg)
   174  		mq.ingressNormalChan <- mm
   175  	}
   176  }
   177  
   178  func (mq *msgQ) getEgressChan() chan *metaKnownMsg {
   179  	if mq.egressChan == nil {
   180  		return mq.ingressNormalChan
   181  	}
   182  
   183  	// protect reading mq.egressChan while writing it by putMetaMsg
   184  	if !mq.changeConfirmed {
   185  		mq.Lock()
   186  		mq.changeConfirmed = true
   187  		mq.lg.Debugf("msgq egressChan change confirmed")
   188  		mq.Unlock()
   189  	}
   190  
   191  	return mq.egressChan
   192  }