-
Notifications
You must be signed in to change notification settings - Fork 927
/
redislock.go
60 lines (49 loc) · 1.24 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
package common
import (
"strconv"
"time"
"emperror.dev/errors"
"github.com/jonas747/retryableredis"
)
// Locks the lock and if succeded sets it to expire after maxdur
// So that if someting went wrong its not locked forever
func TryLockRedisKey(key string, maxDur int) (bool, error) {
resp := ""
err := RedisPool.Do(retryableredis.Cmd(&resp, "SET", key, "1", "NX", "EX", strconv.Itoa(maxDur)))
if err != nil {
return false, err
}
if resp == "OK" {
return true, nil
}
return false, nil
}
var (
ErrMaxLockAttemptsExceeded = errors.New("Max lock attempts exceeded")
)
// BlockingLockRedisKey blocks until it suceeded to lock the key
func BlockingLockRedisKey(key string, maxTryDuration time.Duration, maxLockDur int) error {
started := time.Now()
sleepDur := time.Millisecond * 100
maxSleep := time.Second
for {
if maxTryDuration != 0 && time.Since(started) > maxTryDuration {
return ErrMaxLockAttemptsExceeded
}
locked, err := TryLockRedisKey(key, maxLockDur)
if err != nil {
return ErrWithCaller(err)
}
if locked {
return nil
}
time.Sleep(sleepDur)
sleepDur *= 2
if sleepDur > maxSleep {
sleepDur = maxSleep
}
}
}
func UnlockRedisKey(key string) {
RedisPool.Do(retryableredis.Cmd(nil, "DEL", key))
}