github.com/projecteru2/core@v0.0.0-20240321043226-06bcc1c23f58/lock/redis/lock.go (about)

     1  package redislock
     2  
     3  import (
     4  	"context"
     5  	"strings"
     6  	"time"
     7  
     8  	"github.com/muroq/redislock"
     9  	"github.com/projecteru2/core/types"
    10  )
    11  
    12  var opts = &redislock.Options{
    13  	RetryStrategy: redislock.LinearBackoff(500 * time.Millisecond),
    14  }
    15  
    16  // RedisLock is a redis SET NX based lock
    17  type RedisLock struct {
    18  	key     string
    19  	timeout time.Duration
    20  	ttl     time.Duration
    21  	lc      *redislock.Client
    22  	l       *redislock.Lock
    23  }
    24  
    25  // New creates a lock
    26  // key: name of the lock
    27  // waitTimeout: timeout before getting the lock, Lock returns error if the lock is not acquired after this time
    28  // lockTTL: ttl of lock, after this time, lock will be released automatically
    29  func New(cli redislock.RedisClient, key string, waitTimeout, lockTTL time.Duration) (*RedisLock, error) {
    30  	if key == "" {
    31  		return nil, types.ErrLockKeyInvaild
    32  	}
    33  
    34  	if !strings.HasPrefix(key, "/") {
    35  		key = "/" + key
    36  	}
    37  
    38  	locker := redislock.New(cli)
    39  	return &RedisLock{
    40  		key:     key,
    41  		timeout: waitTimeout,
    42  		ttl:     lockTTL,
    43  		lc:      locker,
    44  	}, nil
    45  }
    46  
    47  // Lock acquires the lock
    48  // will try waitTimeout time before getting the lock
    49  func (r *RedisLock) Lock(ctx context.Context) (context.Context, error) {
    50  	lockCtx, cancel := context.WithTimeout(ctx, r.timeout)
    51  	defer cancel()
    52  	return r.lock(lockCtx, opts)
    53  }
    54  
    55  // TryLock tries to lock
    56  // returns error if the lock is already acquired by someone else
    57  // will not retry to get lock
    58  func (r *RedisLock) TryLock(ctx context.Context) (context.Context, error) {
    59  	return r.lock(ctx, nil)
    60  }
    61  
    62  // Unlock releases the lock
    63  // if the lock is not acquired, will return ErrLockNotHeld
    64  func (r *RedisLock) Unlock(ctx context.Context) error {
    65  	if r.l == nil {
    66  		return redislock.ErrLockNotHeld
    67  	}
    68  
    69  	lockCtx, cancel := context.WithTimeout(ctx, r.ttl)
    70  	defer cancel()
    71  	return r.l.Release(lockCtx)
    72  }
    73  
    74  func (r *RedisLock) lock(ctx context.Context, opts *redislock.Options) (context.Context, error) {
    75  	l, err := r.lc.Obtain(ctx, r.key, r.timeout, r.ttl, opts)
    76  	if err != nil {
    77  		return nil, err
    78  	}
    79  
    80  	r.l = l
    81  	return context.TODO(), nil // no need wrapped, not like etcd
    82  }