code.gitea.io/gitea@v1.19.3/modules/queue/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  	"code.gitea.io/gitea/modules/graceful"
    10  	"code.gitea.io/gitea/modules/log"
    11  	"code.gitea.io/gitea/modules/nosql"
    12  
    13  	"github.com/redis/go-redis/v9"
    14  )
    15  
    16  // RedisQueueType is the type for redis queue
    17  const RedisQueueType Type = "redis"
    18  
    19  // RedisQueueConfiguration is the configuration for the redis queue
    20  type RedisQueueConfiguration struct {
    21  	ByteFIFOQueueConfiguration
    22  	RedisByteFIFOConfiguration
    23  }
    24  
    25  // RedisQueue redis queue
    26  type RedisQueue struct {
    27  	*ByteFIFOQueue
    28  }
    29  
    30  // NewRedisQueue creates single redis or cluster redis queue
    31  func NewRedisQueue(handle HandlerFunc, cfg, exemplar interface{}) (Queue, error) {
    32  	configInterface, err := toConfig(RedisQueueConfiguration{}, cfg)
    33  	if err != nil {
    34  		return nil, err
    35  	}
    36  	config := configInterface.(RedisQueueConfiguration)
    37  
    38  	byteFIFO, err := NewRedisByteFIFO(config.RedisByteFIFOConfiguration)
    39  	if err != nil {
    40  		return nil, err
    41  	}
    42  
    43  	byteFIFOQueue, err := NewByteFIFOQueue(RedisQueueType, byteFIFO, handle, config.ByteFIFOQueueConfiguration, exemplar)
    44  	if err != nil {
    45  		return nil, err
    46  	}
    47  
    48  	queue := &RedisQueue{
    49  		ByteFIFOQueue: byteFIFOQueue,
    50  	}
    51  
    52  	queue.qid = GetManager().Add(queue, RedisQueueType, config, exemplar)
    53  
    54  	return queue, nil
    55  }
    56  
    57  type redisClient interface {
    58  	RPush(ctx context.Context, key string, args ...interface{}) *redis.IntCmd
    59  	LPush(ctx context.Context, key string, args ...interface{}) *redis.IntCmd
    60  	LPop(ctx context.Context, key string) *redis.StringCmd
    61  	LLen(ctx context.Context, key string) *redis.IntCmd
    62  	SAdd(ctx context.Context, key string, members ...interface{}) *redis.IntCmd
    63  	SRem(ctx context.Context, key string, members ...interface{}) *redis.IntCmd
    64  	SIsMember(ctx context.Context, key string, member interface{}) *redis.BoolCmd
    65  	Ping(ctx context.Context) *redis.StatusCmd
    66  	Close() error
    67  }
    68  
    69  var _ ByteFIFO = &RedisByteFIFO{}
    70  
    71  // RedisByteFIFO represents a ByteFIFO formed from a redisClient
    72  type RedisByteFIFO struct {
    73  	client redisClient
    74  
    75  	queueName string
    76  }
    77  
    78  // RedisByteFIFOConfiguration is the configuration for the RedisByteFIFO
    79  type RedisByteFIFOConfiguration struct {
    80  	ConnectionString string
    81  	QueueName        string
    82  }
    83  
    84  // NewRedisByteFIFO creates a ByteFIFO formed from a redisClient
    85  func NewRedisByteFIFO(config RedisByteFIFOConfiguration) (*RedisByteFIFO, error) {
    86  	fifo := &RedisByteFIFO{
    87  		queueName: config.QueueName,
    88  	}
    89  	fifo.client = nosql.GetManager().GetRedisClient(config.ConnectionString)
    90  	if err := fifo.client.Ping(graceful.GetManager().ShutdownContext()).Err(); err != nil {
    91  		return nil, err
    92  	}
    93  	return fifo, nil
    94  }
    95  
    96  // PushFunc pushes data to the end of the fifo and calls the callback if it is added
    97  func (fifo *RedisByteFIFO) PushFunc(ctx context.Context, data []byte, fn func() error) error {
    98  	if fn != nil {
    99  		if err := fn(); err != nil {
   100  			return err
   101  		}
   102  	}
   103  	return fifo.client.RPush(ctx, fifo.queueName, data).Err()
   104  }
   105  
   106  // PushBack pushes data to the top of the fifo
   107  func (fifo *RedisByteFIFO) PushBack(ctx context.Context, data []byte) error {
   108  	return fifo.client.LPush(ctx, fifo.queueName, data).Err()
   109  }
   110  
   111  // Pop pops data from the start of the fifo
   112  func (fifo *RedisByteFIFO) Pop(ctx context.Context) ([]byte, error) {
   113  	data, err := fifo.client.LPop(ctx, fifo.queueName).Bytes()
   114  	if err == nil || err == redis.Nil {
   115  		return data, nil
   116  	}
   117  	return data, err
   118  }
   119  
   120  // Close this fifo
   121  func (fifo *RedisByteFIFO) Close() error {
   122  	return fifo.client.Close()
   123  }
   124  
   125  // Len returns the length of the fifo
   126  func (fifo *RedisByteFIFO) Len(ctx context.Context) int64 {
   127  	val, err := fifo.client.LLen(ctx, fifo.queueName).Result()
   128  	if err != nil {
   129  		log.Error("Error whilst getting length of redis queue %s: Error: %v", fifo.queueName, err)
   130  		return -1
   131  	}
   132  	return val
   133  }
   134  
   135  func init() {
   136  	queuesMap[RedisQueueType] = NewRedisQueue
   137  }