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  }