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 }