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