vitess.io/vitess@v0.16.2/go/vt/vtorc/discovery/queue.go (about) 1 /* 2 Copyright 2014 Outbrain Inc. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 /* 18 19 package discovery manages a queue of discovery requests: an ordered 20 queue with no duplicates. 21 22 push() operation never blocks while pop() blocks on an empty queue. 23 24 */ 25 26 package discovery 27 28 import ( 29 "sync" 30 "time" 31 32 "vitess.io/vitess/go/vt/log" 33 "vitess.io/vitess/go/vt/vtorc/config" 34 "vitess.io/vitess/go/vt/vtorc/inst" 35 ) 36 37 // QueueMetric contains the queue's active and queued sizes 38 type QueueMetric struct { 39 Active int 40 Queued int 41 } 42 43 // Queue contains information for managing discovery requests 44 type Queue struct { 45 sync.Mutex 46 47 name string 48 done chan struct{} 49 queue chan inst.InstanceKey 50 queuedKeys map[inst.InstanceKey]time.Time 51 consumedKeys map[inst.InstanceKey]time.Time 52 metrics []QueueMetric 53 } 54 55 // DiscoveryQueue contains the discovery queue which can then be accessed via an API call for monitoring. 56 // Currently this is accessed by ContinuousDiscovery() but also from http api calls. 57 // I may need to protect this better? 58 var discoveryQueue map[string](*Queue) 59 var dcLock sync.Mutex 60 61 func init() { 62 discoveryQueue = make(map[string](*Queue)) 63 } 64 65 // StopMonitoring stops monitoring all the queues 66 func StopMonitoring() { 67 for _, q := range discoveryQueue { 68 q.stopMonitoring() 69 } 70 } 71 72 // CreateOrReturnQueue allows for creation of a new discovery queue or 73 // returning a pointer to an existing one given the name. 74 func CreateOrReturnQueue(name string) *Queue { 75 dcLock.Lock() 76 defer dcLock.Unlock() 77 if q, found := discoveryQueue[name]; found { 78 return q 79 } 80 81 q := &Queue{ 82 name: name, 83 queuedKeys: make(map[inst.InstanceKey]time.Time), 84 consumedKeys: make(map[inst.InstanceKey]time.Time), 85 queue: make(chan inst.InstanceKey, config.DiscoveryQueueCapacity), 86 } 87 go q.startMonitoring() 88 89 discoveryQueue[name] = q 90 91 return q 92 } 93 94 // monitoring queue sizes until we are told to stop 95 func (q *Queue) startMonitoring() { 96 log.Infof("Queue.startMonitoring(%s)", q.name) 97 ticker := time.NewTicker(time.Second) // hard-coded at every second 98 99 for { 100 select { 101 case <-ticker.C: // do the periodic expiry 102 q.collectStatistics() 103 case <-q.done: 104 return 105 } 106 } 107 } 108 109 // Stop monitoring the queue 110 func (q *Queue) stopMonitoring() { 111 q.done <- struct{}{} 112 } 113 114 // do a check of the entries in the queue, both those active and queued 115 func (q *Queue) collectStatistics() { 116 q.Lock() 117 defer q.Unlock() 118 119 q.metrics = append(q.metrics, QueueMetric{Queued: len(q.queuedKeys), Active: len(q.consumedKeys)}) 120 121 // remove old entries if we get too big 122 if len(q.metrics) > config.DiscoveryQueueMaxStatisticsSize { 123 q.metrics = q.metrics[len(q.metrics)-config.DiscoveryQueueMaxStatisticsSize:] 124 } 125 } 126 127 // QueueLen returns the length of the queue (channel size + queued size) 128 func (q *Queue) QueueLen() int { 129 q.Lock() 130 defer q.Unlock() 131 132 return len(q.queue) + len(q.queuedKeys) 133 } 134 135 // Push enqueues a key if it is not on a queue and is not being 136 // processed; silently returns otherwise. 137 func (q *Queue) Push(key inst.InstanceKey) { 138 q.Lock() 139 defer q.Unlock() 140 141 // is it enqueued already? 142 if _, found := q.queuedKeys[key]; found { 143 return 144 } 145 146 // is it being processed now? 147 if _, found := q.consumedKeys[key]; found { 148 return 149 } 150 151 q.queuedKeys[key] = time.Now() 152 q.queue <- key 153 } 154 155 // Consume fetches a key to process; blocks if queue is empty. 156 // Release must be called once after Consume. 157 func (q *Queue) Consume() inst.InstanceKey { 158 q.Lock() 159 queue := q.queue 160 q.Unlock() 161 162 key := <-queue 163 164 q.Lock() 165 defer q.Unlock() 166 167 // alarm if have been waiting for too long 168 timeOnQueue := time.Since(q.queuedKeys[key]) 169 if timeOnQueue > time.Duration(config.Config.InstancePollSeconds)*time.Second { 170 log.Warningf("key %v spent %.4fs waiting on a discoveryQueue", key, timeOnQueue.Seconds()) 171 } 172 173 q.consumedKeys[key] = q.queuedKeys[key] 174 175 delete(q.queuedKeys, key) 176 177 return key 178 } 179 180 // Release removes a key from a list of being processed keys 181 // which allows that key to be pushed into the queue again. 182 func (q *Queue) Release(key inst.InstanceKey) { 183 q.Lock() 184 defer q.Unlock() 185 186 delete(q.consumedKeys, key) 187 }