-
-
Notifications
You must be signed in to change notification settings - Fork 8
/
redislock.go
86 lines (72 loc) · 2.31 KB
/
redislock.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
85
86
package redislock
import (
"context"
"errors"
"fmt"
"github.com/go-co-op/gocron/v2"
"github.com/go-redsync/redsync/v4"
"github.com/go-redsync/redsync/v4/redis/goredis/v9"
"github.com/redis/go-redis/v9"
)
// alias options
var (
WithExpiry = redsync.WithExpiry
WithDriftFactor = redsync.WithDriftFactor
WithGenValueFunc = redsync.WithGenValueFunc
WithRetryDelay = redsync.WithRetryDelay
WithRetryDelayFunc = redsync.WithRetryDelayFunc
WithTimeoutFactor = redsync.WithTimeoutFactor
WithTries = redsync.WithTries
WithValue = redsync.WithValue
ErrFailedToConnectToRedis = errors.New("gocron: failed to connect to redis")
ErrFailedToObtainLock = errors.New("gocron: failed to obtain lock")
ErrFailedToReleaseLock = errors.New("gocron: failed to release lock")
)
// NewRedisLocker provides an implementation of the Locker interface using
// redis for storage.
func NewRedisLocker(r redis.UniversalClient, options ...redsync.Option) (gocron.Locker, error) {
if err := r.Ping(context.Background()).Err(); err != nil {
return nil, fmt.Errorf("%s: %w", ErrFailedToConnectToRedis, err)
}
return newLocker(r, options...), nil
}
// NewRedisLockerAlways provides an implementation of the Locker interface using
// redis for storage, even if the connection fails.
func NewRedisLockerAlways(r redis.UniversalClient, options ...redsync.Option) (gocron.Locker, error) {
return newLocker(r, options...), r.Ping(context.Background()).Err()
}
func newLocker(r redis.UniversalClient, options ...redsync.Option) gocron.Locker {
pool := goredis.NewPool(r)
rs := redsync.New(pool)
return &redisLocker{rs: rs, options: options}
}
var _ gocron.Locker = (*redisLocker)(nil)
type redisLocker struct {
rs *redsync.Redsync
options []redsync.Option
}
func (r *redisLocker) Lock(ctx context.Context, key string) (gocron.Lock, error) {
mu := r.rs.NewMutex(key, r.options...)
err := mu.LockContext(ctx)
if err != nil {
return nil, ErrFailedToObtainLock
}
rl := &redisLock{
mu: mu,
}
return rl, nil
}
var _ gocron.Lock = (*redisLock)(nil)
type redisLock struct {
mu *redsync.Mutex
}
func (r *redisLock) Unlock(ctx context.Context) error {
unlocked, err := r.mu.UnlockContext(ctx)
if err != nil {
return ErrFailedToReleaseLock
}
if !unlocked {
return ErrFailedToReleaseLock
}
return nil
}