@@ -3,6 +3,7 @@ package candiutils
3
3
import (
4
4
"context"
5
5
"errors"
6
+ "fmt"
6
7
"time"
7
8
8
9
"github.com/gomodule/redigo/redis"
@@ -13,76 +14,161 @@ import (
13
14
type (
14
15
// RedisLocker lock using redis
15
16
RedisLocker struct {
16
- pool * redis.Pool
17
+ pool * redis.Pool
18
+ lockeroptions LockerOptions
17
19
}
18
20
19
21
// NoopLocker empty locker
20
22
NoopLocker struct {}
23
+
24
+ // Options for RedisLocker
25
+ LockerOptions struct {
26
+ Prefix string
27
+ TTL time.Duration
28
+ }
29
+
30
+ // Option function type for setting options
31
+ LockerOption func (* LockerOptions )
21
32
)
22
33
34
+ // WithPrefix sets the prefix for keys
35
+ func WithPrefixLocker (prefix string ) LockerOption {
36
+ return func (o * LockerOptions ) {
37
+ o .Prefix = prefix
38
+ }
39
+ }
40
+
41
+ // WithTTL sets the default TTL for keys
42
+ func WithTTLLocker (ttl time.Duration ) LockerOption {
43
+ return func (o * LockerOptions ) {
44
+ o .TTL = ttl
45
+ }
46
+ }
47
+
23
48
// NewRedisLocker constructor
24
- func NewRedisLocker (pool * redis.Pool ) * RedisLocker {
25
- return & RedisLocker {pool : pool }
49
+ func NewRedisLocker (pool * redis.Pool , opts ... LockerOption ) * RedisLocker {
50
+ lockeroptions := LockerOptions {
51
+ Prefix : "LOCKFOR" ,
52
+ TTL : 0 ,
53
+ }
54
+ for _ , opt := range opts {
55
+ opt (& lockeroptions )
56
+ }
57
+ return & RedisLocker {pool : pool , lockeroptions : lockeroptions }
58
+ }
59
+
60
+ // GetPrefix returns the prefix used for keys
61
+ func (r * RedisLocker ) GetPrefixLocker () string {
62
+ return r .lockeroptions .Prefix + ":"
63
+ }
64
+
65
+ // GetTTLLocker returns the default TTL for keys
66
+ func (r * RedisLocker ) GetTTLLocker () time.Duration {
67
+ return r .lockeroptions .TTL
68
+ }
69
+
70
+ func (r * RedisLocker ) IsLocked (key string ) bool {
71
+ conn := r .pool .Get ()
72
+ defer conn .Close ()
73
+
74
+ lockKey := fmt .Sprintf ("%s:%s" , r .lockeroptions .Prefix , key )
75
+ incr , err := redis .Int64 (conn .Do ("INCR" , lockKey ))
76
+ if err != nil {
77
+ return false
78
+ }
79
+
80
+ return incr > 1
26
81
}
27
82
28
- func (r * RedisLocker ) IsLocked (key string , expiration ... time.Duration ) bool {
83
+ func (r * RedisLocker ) IsLockedTTL (key string , TTL time.Duration ) bool {
29
84
conn := r .pool .Get ()
30
- incr , _ := redis .Int64 (conn .Do ("INCR" , key ))
85
+ defer conn .Close ()
86
+
87
+ lockKey := fmt .Sprintf ("%s:%s" , r .lockeroptions .Prefix , key )
88
+ incr , err := redis .Int64 (conn .Do ("INCR" , lockKey ))
89
+ if err != nil {
90
+ return false
91
+ }
31
92
32
- if len (expiration ) > 0 {
33
- conn .Do ("EXPIRE" , key , int (expiration [0 ].Seconds ()))
93
+ var expireTime time.Duration
94
+ if TTL > 0 {
95
+ expireTime = TTL
96
+ } else {
97
+ expireTime = r .lockeroptions .TTL
98
+ }
99
+
100
+ if expireTime > 0 {
101
+ conn .Do ("EXPIRE" , lockKey , int (expireTime .Seconds ()))
34
102
}
35
- conn .Close ()
36
103
37
104
return incr > 1
38
105
}
39
106
40
107
func (r * RedisLocker ) HasBeenLocked (key string ) bool {
41
108
conn := r .pool .Get ()
42
- incr , _ := redis .Int64 (conn .Do ("GET" , key ))
43
- conn .Close ()
109
+ defer conn .Close ()
110
+
111
+ lockKey := fmt .Sprintf ("%s:%s" , r .lockeroptions .Prefix , key )
112
+ incr , _ := redis .Int64 (conn .Do ("GET" , lockKey ))
44
113
45
114
return incr > 0
46
115
}
47
116
48
117
// Unlock method
49
118
func (r * RedisLocker ) Unlock (key string ) {
50
119
conn := r .pool .Get ()
51
- conn .Do ("DEL" , key )
52
- conn .Close ()
120
+ defer conn .Close ()
121
+
122
+ lockKey := fmt .Sprintf ("%s:%s" , r .lockeroptions .Prefix , key )
123
+ conn .Do ("DEL" , lockKey )
53
124
}
54
125
55
126
// Reset method
56
127
func (r * RedisLocker ) Reset (key string ) {
57
128
conn := r .pool .Get ()
58
- keys , _ := redis .Strings (conn .Do ("KEYS" , key ))
129
+ defer conn .Close ()
130
+
131
+ lockKey := fmt .Sprintf ("%s:%s" , r .lockeroptions .Prefix , key )
132
+ keys , err := redis .Strings (conn .Do ("KEYS" , lockKey ))
133
+ if err != nil {
134
+ fmt .Println ("Error when reset locker: " , key , err )
135
+ return
136
+ }
137
+
59
138
for _ , k := range keys {
60
- conn .Do ("DEL" , k )
139
+ _ , err := conn .Do ("DEL" , k )
140
+ if err != nil {
141
+ fmt .Println ("Error when reset locker: " , key , err )
142
+ }
61
143
}
62
- conn .Close ()
63
- return
64
144
}
65
145
66
146
// Disconnect close and reset
67
147
func (r * RedisLocker ) Disconnect (ctx context.Context ) error {
68
148
conn := r .pool .Get ()
69
- conn .Do ("DEL" , "LOCKFOR:*" )
70
- conn .Close ()
149
+ defer conn .Close ()
150
+
151
+ lockKey := fmt .Sprintf ("%s:*" , r .lockeroptions .Prefix )
152
+ _ , err := conn .Do ("DEL" , lockKey )
153
+ if err != nil {
154
+ return err
155
+ }
156
+
71
157
return nil
72
158
}
73
159
74
160
// Lock method
75
161
func (r * RedisLocker ) Lock (key string , timeout time.Duration ) (unlockFunc func (), err error ) {
76
162
if timeout <= 0 {
77
- return func () {}, errors .New ("Timeout must be positive" )
163
+ return func () {}, errors .New ("timeout must be positive" )
78
164
}
79
165
if key == "" {
80
- return func () {}, errors .New ("Key cannot empty" )
166
+ return func () {}, errors .New ("key cannot empty" )
81
167
}
82
168
83
- lockKey := "LOCKFOR:" + key
84
- unlockFunc = func () { r .Unlock (lockKey ) }
85
- if ! r .IsLocked (lockKey ) {
169
+ lockKey := fmt . Sprintf ( "%s:%s" , r . lockeroptions . Prefix , key )
170
+ unlockFunc = func () { r .Unlock (key ) }
171
+ if ! r .IsLocked (key ) {
86
172
return unlockFunc , nil
87
173
}
88
174
@@ -99,7 +185,7 @@ func (r *RedisLocker) Lock(key string, timeout time.Duration) (unlockFunc func()
99
185
for {
100
186
switch msg := psc .Receive ().(type ) {
101
187
case redis.Message :
102
- if msg .Pattern == eventChannel && lockKey == string (msg .Data ) && ! r .IsLocked (lockKey ) {
188
+ if msg .Pattern == eventChannel && lockKey == string (msg .Data ) && ! r .IsLocked (key ) {
103
189
wait <- nil
104
190
return
105
191
}
@@ -116,8 +202,8 @@ func (r *RedisLocker) Lock(key string, timeout time.Duration) (unlockFunc func()
116
202
return unlockFunc , err
117
203
118
204
case <- time .After (timeout ):
119
- r .Unlock (lockKey )
120
- return unlockFunc , errors .New ("Timeout when waiting unlock another process" )
205
+ r .Unlock (key )
206
+ return unlockFunc , errors .New ("timeout when waiting unlock another process" )
121
207
}
122
208
}
123
209
@@ -126,8 +212,8 @@ func (r *RedisLocker) Lock(key string, timeout time.Duration) (unlockFunc func()
126
212
// IsLocked method
127
213
func (NoopLocker ) IsLocked (string ) bool { return false }
128
214
129
- // IsLocked method
130
- func (NoopLocker ) IsLockedWithTTL (string , time.Duration ) bool { return false }
215
+ // IsLockedTTL method
216
+ func (NoopLocker ) IsLockedTTL (string , time.Duration ) bool { return false }
131
217
132
218
// HasBeenLocked method
133
219
func (NoopLocker ) HasBeenLocked (string ) bool { return false }
@@ -142,3 +228,9 @@ func (NoopLocker) Reset(string) {}
142
228
func (NoopLocker ) Lock (string , time.Duration ) (func (), error ) { return func () {}, nil }
143
229
144
230
func (NoopLocker ) Disconnect (context.Context ) error { return nil }
231
+
232
+ // GetPrefix method
233
+ func (NoopLocker ) GetPrefixLocker () string { return "" }
234
+
235
+ // GetTTLLocker method
236
+ func (NoopLocker ) GetTTLLocker () time.Duration { return 0 }
0 commit comments