code.gitea.io/gitea@v1.19.3/modules/queue/unique_queue_disk_channel.go (about) 1 // Copyright 2020 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package queue 5 6 import ( 7 "context" 8 "runtime/pprof" 9 "sync" 10 "time" 11 12 "code.gitea.io/gitea/modules/log" 13 ) 14 15 // PersistableChannelUniqueQueueType is the type for persistable queue 16 const PersistableChannelUniqueQueueType Type = "unique-persistable-channel" 17 18 // PersistableChannelUniqueQueueConfiguration is the configuration for a PersistableChannelUniqueQueue 19 type PersistableChannelUniqueQueueConfiguration struct { 20 Name string 21 DataDir string 22 BatchLength int 23 QueueLength int 24 Timeout time.Duration 25 MaxAttempts int 26 Workers int 27 MaxWorkers int 28 BlockTimeout time.Duration 29 BoostTimeout time.Duration 30 BoostWorkers int 31 } 32 33 // PersistableChannelUniqueQueue wraps a channel queue and level queue together 34 // 35 // Please note that this Queue does not guarantee that a particular 36 // task cannot be processed twice or more at the same time. Uniqueness is 37 // only guaranteed whilst the task is waiting in the queue. 38 type PersistableChannelUniqueQueue struct { 39 channelQueue *ChannelUniqueQueue 40 delayedStarter 41 lock sync.Mutex 42 closed chan struct{} 43 } 44 45 // NewPersistableChannelUniqueQueue creates a wrapped batched channel queue with persistable level queue backend when shutting down 46 // This differs from a wrapped queue in that the persistent queue is only used to persist at shutdown/terminate 47 func NewPersistableChannelUniqueQueue(handle HandlerFunc, cfg, exemplar interface{}) (Queue, error) { 48 configInterface, err := toConfig(PersistableChannelUniqueQueueConfiguration{}, cfg) 49 if err != nil { 50 return nil, err 51 } 52 config := configInterface.(PersistableChannelUniqueQueueConfiguration) 53 54 queue := &PersistableChannelUniqueQueue{ 55 closed: make(chan struct{}), 56 } 57 58 wrappedHandle := func(data ...Data) (failed []Data) { 59 for _, unhandled := range handle(data...) { 60 if fail := queue.PushBack(unhandled); fail != nil { 61 failed = append(failed, fail) 62 } 63 } 64 return failed 65 } 66 67 channelUniqueQueue, err := NewChannelUniqueQueue(wrappedHandle, ChannelUniqueQueueConfiguration{ 68 WorkerPoolConfiguration: WorkerPoolConfiguration{ 69 QueueLength: config.QueueLength, 70 BatchLength: config.BatchLength, 71 BlockTimeout: config.BlockTimeout, 72 BoostTimeout: config.BoostTimeout, 73 BoostWorkers: config.BoostWorkers, 74 MaxWorkers: config.MaxWorkers, 75 Name: config.Name + "-channel", 76 }, 77 Workers: config.Workers, 78 }, exemplar) 79 if err != nil { 80 return nil, err 81 } 82 83 // the level backend only needs temporary workers to catch up with the previously dropped work 84 levelCfg := LevelUniqueQueueConfiguration{ 85 ByteFIFOQueueConfiguration: ByteFIFOQueueConfiguration{ 86 WorkerPoolConfiguration: WorkerPoolConfiguration{ 87 QueueLength: config.QueueLength, 88 BatchLength: config.BatchLength, 89 BlockTimeout: 1 * time.Second, 90 BoostTimeout: 5 * time.Minute, 91 BoostWorkers: 1, 92 MaxWorkers: 5, 93 Name: config.Name + "-level", 94 }, 95 Workers: 0, 96 }, 97 DataDir: config.DataDir, 98 QueueName: config.Name + "-level", 99 } 100 101 queue.channelQueue = channelUniqueQueue.(*ChannelUniqueQueue) 102 103 levelQueue, err := NewLevelUniqueQueue(func(data ...Data) []Data { 104 for _, datum := range data { 105 err := queue.Push(datum) 106 if err != nil && err != ErrAlreadyInQueue { 107 log.Error("Unable push to channelled queue: %v", err) 108 } 109 } 110 return nil 111 }, levelCfg, exemplar) 112 if err == nil { 113 queue.delayedStarter = delayedStarter{ 114 internal: levelQueue.(*LevelUniqueQueue), 115 name: config.Name, 116 } 117 118 _ = GetManager().Add(queue, PersistableChannelUniqueQueueType, config, exemplar) 119 return queue, nil 120 } 121 if IsErrInvalidConfiguration(err) { 122 // Retrying ain't gonna make this any better... 123 return nil, ErrInvalidConfiguration{cfg: cfg} 124 } 125 126 queue.delayedStarter = delayedStarter{ 127 cfg: levelCfg, 128 underlying: LevelUniqueQueueType, 129 timeout: config.Timeout, 130 maxAttempts: config.MaxAttempts, 131 name: config.Name, 132 } 133 _ = GetManager().Add(queue, PersistableChannelUniqueQueueType, config, exemplar) 134 return queue, nil 135 } 136 137 // Name returns the name of this queue 138 func (q *PersistableChannelUniqueQueue) Name() string { 139 return q.delayedStarter.name 140 } 141 142 // Push will push the indexer data to queue 143 func (q *PersistableChannelUniqueQueue) Push(data Data) error { 144 return q.PushFunc(data, nil) 145 } 146 147 // PushFunc will push the indexer data to queue 148 func (q *PersistableChannelUniqueQueue) PushFunc(data Data, fn func() error) error { 149 select { 150 case <-q.closed: 151 return q.internal.(UniqueQueue).PushFunc(data, fn) 152 default: 153 return q.channelQueue.PushFunc(data, fn) 154 } 155 } 156 157 // PushBack will push the indexer data to queue 158 func (q *PersistableChannelUniqueQueue) PushBack(data Data) error { 159 select { 160 case <-q.closed: 161 if pbr, ok := q.internal.(PushBackable); ok { 162 return pbr.PushBack(data) 163 } 164 return q.internal.Push(data) 165 default: 166 return q.channelQueue.Push(data) 167 } 168 } 169 170 // Has will test if the queue has the data 171 func (q *PersistableChannelUniqueQueue) Has(data Data) (bool, error) { 172 // This is more difficult... 173 has, err := q.channelQueue.Has(data) 174 if err != nil || has { 175 return has, err 176 } 177 q.lock.Lock() 178 defer q.lock.Unlock() 179 if q.internal == nil { 180 return false, nil 181 } 182 return q.internal.(UniqueQueue).Has(data) 183 } 184 185 // Run starts to run the queue 186 func (q *PersistableChannelUniqueQueue) Run(atShutdown, atTerminate func(func())) { 187 pprof.SetGoroutineLabels(q.channelQueue.baseCtx) 188 log.Debug("PersistableChannelUniqueQueue: %s Starting", q.delayedStarter.name) 189 190 q.lock.Lock() 191 if q.internal == nil { 192 err := q.setInternal(atShutdown, func(data ...Data) []Data { 193 for _, datum := range data { 194 err := q.Push(datum) 195 if err != nil && err != ErrAlreadyInQueue { 196 log.Error("Unable push to channelled queue: %v", err) 197 } 198 } 199 return nil 200 }, q.channelQueue.exemplar) 201 q.lock.Unlock() 202 if err != nil { 203 log.Fatal("Unable to create internal queue for %s Error: %v", q.Name(), err) 204 return 205 } 206 } else { 207 q.lock.Unlock() 208 } 209 atShutdown(q.Shutdown) 210 atTerminate(q.Terminate) 211 _ = q.channelQueue.AddWorkers(q.channelQueue.workers, 0) 212 213 if luq, ok := q.internal.(*LevelUniqueQueue); ok && !luq.IsEmpty() { 214 // Just run the level queue - we shut it down once it's flushed 215 go luq.Run(func(_ func()) {}, func(_ func()) {}) 216 go func() { 217 _ = luq.Flush(0) 218 for !luq.IsEmpty() { 219 _ = luq.Flush(0) 220 select { 221 case <-time.After(100 * time.Millisecond): 222 case <-luq.shutdownCtx.Done(): 223 if luq.byteFIFO.Len(luq.terminateCtx) > 0 { 224 log.Warn("LevelUniqueQueue: %s shut down before completely flushed", luq.Name()) 225 } 226 return 227 } 228 } 229 log.Debug("LevelUniqueQueue: %s flushed so shutting down", luq.Name()) 230 luq.Shutdown() 231 GetManager().Remove(luq.qid) 232 }() 233 } else { 234 log.Debug("PersistableChannelUniqueQueue: %s Skipping running the empty level queue", q.delayedStarter.name) 235 _ = q.internal.Flush(0) 236 q.internal.(*LevelUniqueQueue).Shutdown() 237 GetManager().Remove(q.internal.(*LevelUniqueQueue).qid) 238 } 239 } 240 241 // Flush flushes the queue 242 func (q *PersistableChannelUniqueQueue) Flush(timeout time.Duration) error { 243 return q.channelQueue.Flush(timeout) 244 } 245 246 // FlushWithContext flushes the queue 247 func (q *PersistableChannelUniqueQueue) FlushWithContext(ctx context.Context) error { 248 return q.channelQueue.FlushWithContext(ctx) 249 } 250 251 // IsEmpty checks if a queue is empty 252 func (q *PersistableChannelUniqueQueue) IsEmpty() bool { 253 return q.channelQueue.IsEmpty() 254 } 255 256 // IsPaused will return if the pool or queue is paused 257 func (q *PersistableChannelUniqueQueue) IsPaused() bool { 258 return q.channelQueue.IsPaused() 259 } 260 261 // Pause will pause the pool or queue 262 func (q *PersistableChannelUniqueQueue) Pause() { 263 q.channelQueue.Pause() 264 } 265 266 // Resume will resume the pool or queue 267 func (q *PersistableChannelUniqueQueue) Resume() { 268 q.channelQueue.Resume() 269 } 270 271 // IsPausedIsResumed will return a bool indicating if the pool or queue is paused and a channel that will be closed when it is resumed 272 func (q *PersistableChannelUniqueQueue) IsPausedIsResumed() (paused, resumed <-chan struct{}) { 273 return q.channelQueue.IsPausedIsResumed() 274 } 275 276 // Shutdown processing this queue 277 func (q *PersistableChannelUniqueQueue) Shutdown() { 278 log.Trace("PersistableChannelUniqueQueue: %s Shutting down", q.delayedStarter.name) 279 q.lock.Lock() 280 select { 281 case <-q.closed: 282 q.lock.Unlock() 283 return 284 default: 285 if q.internal != nil { 286 q.internal.(*LevelUniqueQueue).Shutdown() 287 } 288 close(q.closed) 289 q.lock.Unlock() 290 } 291 292 log.Trace("PersistableChannelUniqueQueue: %s Cancelling pools", q.delayedStarter.name) 293 q.internal.(*LevelUniqueQueue).baseCtxCancel() 294 q.channelQueue.baseCtxCancel() 295 log.Trace("PersistableChannelUniqueQueue: %s Waiting til done", q.delayedStarter.name) 296 q.channelQueue.Wait() 297 q.internal.(*LevelUniqueQueue).Wait() 298 // Redirect all remaining data in the chan to the internal channel 299 close(q.channelQueue.dataChan) 300 log.Trace("PersistableChannelUniqueQueue: %s Redirecting remaining data", q.delayedStarter.name) 301 countOK, countLost := 0, 0 302 for data := range q.channelQueue.dataChan { 303 err := q.internal.(*LevelUniqueQueue).Push(data) 304 if err != nil { 305 log.Error("PersistableChannelUniqueQueue: %s Unable redirect %v due to: %v", q.delayedStarter.name, data, err) 306 countLost++ 307 } else { 308 countOK++ 309 } 310 } 311 if countLost > 0 { 312 log.Warn("PersistableChannelUniqueQueue: %s %d will be restored on restart, %d lost", q.delayedStarter.name, countOK, countLost) 313 } else if countOK > 0 { 314 log.Warn("PersistableChannelUniqueQueue: %s %d will be restored on restart", q.delayedStarter.name, countOK) 315 } 316 log.Trace("PersistableChannelUniqueQueue: %s Done Redirecting remaining data", q.delayedStarter.name) 317 318 log.Debug("PersistableChannelUniqueQueue: %s Shutdown", q.delayedStarter.name) 319 } 320 321 // Terminate this queue and close the queue 322 func (q *PersistableChannelUniqueQueue) Terminate() { 323 log.Trace("PersistableChannelUniqueQueue: %s Terminating", q.delayedStarter.name) 324 q.Shutdown() 325 q.lock.Lock() 326 defer q.lock.Unlock() 327 if q.internal != nil { 328 q.internal.(*LevelUniqueQueue).Terminate() 329 } 330 q.channelQueue.baseCtxFinished() 331 log.Debug("PersistableChannelUniqueQueue: %s Terminated", q.delayedStarter.name) 332 } 333 334 func init() { 335 queuesMap[PersistableChannelUniqueQueueType] = NewPersistableChannelUniqueQueue 336 }