code.gitea.io/gitea@v1.19.3/modules/queue/unique_queue_redis.go (about)

     1  // Copyright 2019 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package queue
     5  
     6  import (
     7  	"context"
     8  
     9  	"github.com/redis/go-redis/v9"
    10  )
    11  
    12  // RedisUniqueQueueType is the type for redis queue
    13  const RedisUniqueQueueType Type = "unique-redis"
    14  
    15  // RedisUniqueQueue redis queue
    16  type RedisUniqueQueue struct {
    17  	*ByteFIFOUniqueQueue
    18  }
    19  
    20  // RedisUniqueQueueConfiguration is the configuration for the redis queue
    21  type RedisUniqueQueueConfiguration struct {
    22  	ByteFIFOQueueConfiguration
    23  	RedisUniqueByteFIFOConfiguration
    24  }
    25  
    26  // NewRedisUniqueQueue creates single redis or cluster redis queue.
    27  //
    28  // Please note that this Queue does not guarantee that a particular
    29  // task cannot be processed twice or more at the same time. Uniqueness is
    30  // only guaranteed whilst the task is waiting in the queue.
    31  func NewRedisUniqueQueue(handle HandlerFunc, cfg, exemplar interface{}) (Queue, error) {
    32  	configInterface, err := toConfig(RedisUniqueQueueConfiguration{}, cfg)
    33  	if err != nil {
    34  		return nil, err
    35  	}
    36  	config := configInterface.(RedisUniqueQueueConfiguration)
    37  
    38  	byteFIFO, err := NewRedisUniqueByteFIFO(config.RedisUniqueByteFIFOConfiguration)
    39  	if err != nil {
    40  		return nil, err
    41  	}
    42  
    43  	if len(byteFIFO.setName) == 0 {
    44  		byteFIFO.setName = byteFIFO.queueName + "_unique"
    45  	}
    46  
    47  	byteFIFOQueue, err := NewByteFIFOUniqueQueue(RedisUniqueQueueType, byteFIFO, handle, config.ByteFIFOQueueConfiguration, exemplar)
    48  	if err != nil {
    49  		return nil, err
    50  	}
    51  
    52  	queue := &RedisUniqueQueue{
    53  		ByteFIFOUniqueQueue: byteFIFOQueue,
    54  	}
    55  
    56  	queue.qid = GetManager().Add(queue, RedisUniqueQueueType, config, exemplar)
    57  
    58  	return queue, nil
    59  }
    60  
    61  var _ UniqueByteFIFO = &RedisUniqueByteFIFO{}
    62  
    63  // RedisUniqueByteFIFO represents a UniqueByteFIFO formed from a redisClient
    64  type RedisUniqueByteFIFO struct {
    65  	RedisByteFIFO
    66  	setName string
    67  }
    68  
    69  // RedisUniqueByteFIFOConfiguration is the configuration for the RedisUniqueByteFIFO
    70  type RedisUniqueByteFIFOConfiguration struct {
    71  	RedisByteFIFOConfiguration
    72  	SetName string
    73  }
    74  
    75  // NewRedisUniqueByteFIFO creates a UniqueByteFIFO formed from a redisClient
    76  func NewRedisUniqueByteFIFO(config RedisUniqueByteFIFOConfiguration) (*RedisUniqueByteFIFO, error) {
    77  	internal, err := NewRedisByteFIFO(config.RedisByteFIFOConfiguration)
    78  	if err != nil {
    79  		return nil, err
    80  	}
    81  
    82  	fifo := &RedisUniqueByteFIFO{
    83  		RedisByteFIFO: *internal,
    84  		setName:       config.SetName,
    85  	}
    86  
    87  	return fifo, nil
    88  }
    89  
    90  // PushFunc pushes data to the end of the fifo and calls the callback if it is added
    91  func (fifo *RedisUniqueByteFIFO) PushFunc(ctx context.Context, data []byte, fn func() error) error {
    92  	added, err := fifo.client.SAdd(ctx, fifo.setName, data).Result()
    93  	if err != nil {
    94  		return err
    95  	}
    96  	if added == 0 {
    97  		return ErrAlreadyInQueue
    98  	}
    99  	if fn != nil {
   100  		if err := fn(); err != nil {
   101  			return err
   102  		}
   103  	}
   104  	return fifo.client.RPush(ctx, fifo.queueName, data).Err()
   105  }
   106  
   107  // PushBack pushes data to the top of the fifo
   108  func (fifo *RedisUniqueByteFIFO) PushBack(ctx context.Context, data []byte) error {
   109  	added, err := fifo.client.SAdd(ctx, fifo.setName, data).Result()
   110  	if err != nil {
   111  		return err
   112  	}
   113  	if added == 0 {
   114  		return ErrAlreadyInQueue
   115  	}
   116  	return fifo.client.LPush(ctx, fifo.queueName, data).Err()
   117  }
   118  
   119  // Pop pops data from the start of the fifo
   120  func (fifo *RedisUniqueByteFIFO) Pop(ctx context.Context) ([]byte, error) {
   121  	data, err := fifo.client.LPop(ctx, fifo.queueName).Bytes()
   122  	if err != nil && err != redis.Nil {
   123  		return data, err
   124  	}
   125  
   126  	if len(data) == 0 {
   127  		return data, nil
   128  	}
   129  
   130  	err = fifo.client.SRem(ctx, fifo.setName, data).Err()
   131  	return data, err
   132  }
   133  
   134  // Has returns whether the fifo contains this data
   135  func (fifo *RedisUniqueByteFIFO) Has(ctx context.Context, data []byte) (bool, error) {
   136  	return fifo.client.SIsMember(ctx, fifo.setName, data).Result()
   137  }
   138  
   139  func init() {
   140  	queuesMap[RedisUniqueQueueType] = NewRedisUniqueQueue
   141  }