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 }