Skip to content

Commit 20954ec

Browse files
authored
Optimize the naming of reentrant locks (#29)
* Optimize the naming of reentrant locks * test fix
1 parent 41eafa6 commit 20954ec

10 files changed

+113
-70
lines changed

CHANGELOG.cn.md

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,27 @@
66
- 支持自定义超时时间和自动续期,根据实际需求进行灵活配置
77

88
## v1.0.1
9-
- 修复包名问题
9+
- 修复包名问题
10+
11+
## v1.0.2
12+
-`v1.0.0` 标记废弃 #15
13+
-`codecov/codecov-action` 升级到版本4 #11
14+
15+
## v1.0.3
16+
- 优化Lua脚本 #16
17+
18+
## v1.1.0
19+
- 兼容新版本`redis/go-redis` #17
20+
- 错误统一定义 #18
21+
- 删除未使用的选项方法 #19
22+
- 调整自动续订时间 #20
23+
-`github.com/redis/go-redis/v9``9.5.4` 升级到 `9.6.1` #23
24+
25+
## v1.1.1
26+
- 单元测试覆盖与错误优化 #25
27+
- 错误修复:在并发情况下,token相似会导致多次获取锁 #26
28+
29+
## v1.1.2
30+
- Dependabot 计划间隔每周 #27
31+
- 删除毫无意义的 `sync.Mutex` #28
32+
- 优化可重入锁的命名 #29

CHANGELOG.md

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,27 @@
66
- Support custom timeout and automatic renewal, flexible configuration according to actual needs
77

88
## v1.0.1
9-
- Fix package name issue
9+
- Fix package name issue
10+
11+
## v1.0.2
12+
- Mark `v1.0.0` as deprecated #15
13+
- Upgrade `codecov/codecov-action` to version 4 #11
14+
15+
## v1.0.3
16+
- Optimize Lua scripts #16
17+
18+
## v1.1.0
19+
- Compatible with new version `redis/go-redis` #17
20+
- Unify error definitions #18
21+
- Delete unused option methods #19
22+
- Adjust auto-renewal time #20
23+
- Upgrade `github.com/redis/go-redis/v9` from `9.5.4` to `9.6.1` #23
24+
25+
## v1.1.1
26+
- Unit test coverage and error optimization #25
27+
- Fix: In concurrent situations, similar tokens will cause multiple lock acquisitions #26
28+
29+
## v1.1.2
30+
- Dependabot scheduled every week #27
31+
- Delete meaningless `sync.Mutex` #28
32+
- Optimize the naming of reentrant locks #29

lock.go

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,16 @@ import (
66
"fmt"
77
"github.com/google/uuid"
88
"github.com/redis/go-redis/v9"
9-
"sync"
109
"time"
1110
)
1211

13-
// 默认锁超时时间
14-
const lockTime = 5 * time.Second
15-
1612
type RedisLockInter interface {
1713
// Lock 加锁
1814
Lock() error
19-
// UnLock 解锁
20-
UnLock() error
2115
// SpinLock 自旋锁
2216
SpinLock(timeout time.Duration) error
17+
// UnLock 解锁
18+
UnLock() error
2319
// Renew 手动续期
2420
Renew() error
2521
}
@@ -37,13 +33,11 @@ type RedisLock struct {
3733
isAutoRenew bool
3834
autoRenewCtx context.Context
3935
autoRenewCancel context.CancelFunc
40-
mutex sync.Mutex
4136
}
4237

4338
type Option func(lock *RedisLock)
4439

4540
func New(ctx context.Context, redisClient RedisInter, lockKey string, options ...Option) RedisLockInter {
46-
4741
lock := &RedisLock{
4842
Context: ctx,
4943
redis: redisClient,
@@ -52,14 +46,11 @@ func New(ctx context.Context, redisClient RedisInter, lockKey string, options ..
5246
for _, f := range options {
5347
f(lock)
5448
}
55-
5649
lock.key = lockKey
57-
58-
// token 自动生成
50+
// automatically generate tokens
5951
if lock.token == "" {
6052
lock.token = fmt.Sprintf("lock_token:%s", uuid.New().String())
6153
}
62-
6354
return lock
6455
}
6556

lock_redis.go

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,17 @@ import (
88
)
99

1010
var (
11-
//go:embed lua/lock.lua
12-
lockScript string
13-
//go:embed lua/unLock.lua
14-
unLockScript string
15-
//go:embed lua/renew.lua
16-
renewScript string
11+
//go:embed lua/reentrantLock.lua
12+
reentrantLockScript string
13+
//go:embed lua/reentrantUnLock.lua
14+
reentrantUnLockScript string
15+
//go:embed lua/reentrantRenew.lua
16+
reentrantRenewScript string
1717
)
1818

1919
// Lock 加锁
2020
func (lock *RedisLock) Lock() error {
21-
result, err := lock.redis.Eval(lock.Context, lockScript, []string{lock.key}, lock.token, lock.lockTimeout.Seconds()).Result()
21+
result, err := lock.redis.Eval(lock.Context, reentrantLockScript, []string{lock.key}, lock.token, lock.lockTimeout.Seconds()).Result()
2222

2323
if err != nil {
2424
return ErrException
@@ -42,12 +42,11 @@ func (lock *RedisLock) UnLock() error {
4242
lock.autoRenewCancel()
4343
}
4444

45-
result, err := lock.redis.Eval(lock.Context, unLockScript, []string{lock.key}, lock.token).Result()
45+
result, err := lock.redis.Eval(lock.Context, reentrantUnLockScript, []string{lock.key}, lock.token).Result()
4646

4747
if err != nil {
4848
return ErrException
4949
}
50-
5150
if result != "OK" {
5251
return ErrUnLockFailed
5352
}
@@ -80,12 +79,11 @@ func (lock *RedisLock) SpinLock(timeout time.Duration) error {
8079

8180
// Renew 锁手动续期
8281
func (lock *RedisLock) Renew() error {
83-
res, err := lock.redis.Eval(lock.Context, renewScript, []string{lock.key}, lock.token, lock.lockTimeout.Seconds()).Result()
82+
res, err := lock.redis.Eval(lock.Context, reentrantRenewScript, []string{lock.key}, lock.token, lock.lockTimeout.Seconds()).Result()
8483

8584
if err != nil {
8685
return ErrException
8786
}
88-
8987
if res != "OK" {
9088
return ErrLockRenewFailed
9189
}

lock_redis_local_test.go

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ func TestLockSuccess(t *testing.T) {
2626
lock := New(ctx, db, key, WithToken(token))
2727

2828
// 设置模拟锁获取成功的行为
29-
mock.ExpectEval(lockScript, []string{key}, token, lockTime.Seconds()).SetVal("OK")
29+
mock.ExpectEval(reentrantLockScript, []string{key}, token, lockTime.Seconds()).SetVal("OK")
3030

3131
err := lock.Lock()
3232
if err != nil {
@@ -53,7 +53,7 @@ func TestLockFail(t *testing.T) {
5353

5454
ctx := context.Background()
5555
db, mock := redismock.NewClientMock()
56-
mock.ExpectEval(lockScript, []string{key}, token, lockTime.Seconds()).SetVal("OK")
56+
mock.ExpectEval(reentrantLockScript, []string{key}, token, lockTime.Seconds()).SetVal("OK")
5757

5858
var wg sync.WaitGroup
5959
wg.Add(2)
@@ -111,7 +111,7 @@ func TestUnlockFail(t *testing.T) {
111111
lock := New(ctx, db, key, WithToken(token))
112112

113113
// 加锁逻辑
114-
mock.ExpectEval(lockScript, []string{key}, token, lockTime.Seconds()).SetVal("OK")
114+
mock.ExpectEval(reentrantLockScript, []string{key}, token, lockTime.Seconds()).SetVal("OK")
115115

116116
err := lock.Lock()
117117
if err != nil {
@@ -120,7 +120,7 @@ func TestUnlockFail(t *testing.T) {
120120
}
121121

122122
// 解锁逻辑
123-
mock.ExpectEval(unLockScript, []string{key}, token+"test-2").SetVal(nil) // 模拟解锁失败
123+
mock.ExpectEval(reentrantUnLockScript, []string{key}, token+"test-2").SetVal(nil) // 模拟解锁失败
124124

125125
err = lock.UnLock()
126126

@@ -142,8 +142,8 @@ func TestSpinLockSuccess(t *testing.T) {
142142
token2 := "some_token2"
143143
spinTimeout := time.Duration(5) * time.Second
144144

145-
mock.ExpectEval(lockScript, []string{key}, token, lockTime.Seconds()).SetVal("OK")
146-
mock.ExpectEval(lockScript, []string{key}, token2, lockTime.Seconds()).SetVal("OK")
145+
mock.ExpectEval(reentrantLockScript, []string{key}, token, lockTime.Seconds()).SetVal("OK")
146+
mock.ExpectEval(reentrantLockScript, []string{key}, token2, lockTime.Seconds()).SetVal("OK")
147147

148148
var wg sync.WaitGroup
149149
wg.Add(2)
@@ -192,8 +192,8 @@ func TestSpinLockTimeout(t *testing.T) {
192192
spinTimeout := time.Duration(5) * time.Second
193193
spinTimeout2 := time.Duration(3) * time.Second
194194

195-
mock.ExpectEval(lockScript, []string{key}, token, lockTime.Seconds()).SetVal("OK")
196-
mock.ExpectEval(lockScript, []string{key}, token2, lockTime.Seconds()).SetVal("nil")
195+
mock.ExpectEval(reentrantLockScript, []string{key}, token, lockTime.Seconds()).SetVal("OK")
196+
mock.ExpectEval(reentrantLockScript, []string{key}, token2, lockTime.Seconds()).SetVal("nil")
197197

198198
var wg sync.WaitGroup
199199
wg.Add(2)
@@ -238,8 +238,8 @@ func TestRenewSuccess(t *testing.T) {
238238
key := "test_key_TestRenewSuccess"
239239
token := "some_token"
240240

241-
mock.ExpectEval(lockScript, []string{key}, token, lockTime.Seconds()).SetVal("OK")
242-
mock.ExpectEval(renewScript, []string{key}, token, lockTime.Seconds()).SetVal("OK")
241+
mock.ExpectEval(reentrantLockScript, []string{key}, token, lockTime.Seconds()).SetVal("OK")
242+
mock.ExpectEval(reentrantRenewScript, []string{key}, token, lockTime.Seconds()).SetVal("OK")
243243

244244
// 设置模拟锁续期成功的行为
245245
mock.ExpectExpire(key, lockTime).SetVal(true)
@@ -283,7 +283,7 @@ func TestRenewFail(t *testing.T) {
283283
key := "test_key_TestRenewFail"
284284
token := "some_token"
285285

286-
mock.ExpectEval(lockScript, []string{key}, token, lockTime.Seconds()).SetVal("OK")
286+
mock.ExpectEval(reentrantLockScript, []string{key}, token, lockTime.Seconds()).SetVal("OK")
287287
// 设置模拟锁续期成功的行为
288288
mock.ExpectExpire(key, lockTime).SetVal(false)
289289

@@ -328,7 +328,7 @@ func TestWithTimeout(t *testing.T) {
328328
token := "some_token"
329329
timeout := time.Duration(10) * time.Second
330330

331-
mock.ExpectEval(lockScript, []string{key}, token, timeout.Seconds()).SetVal("OK")
331+
mock.ExpectEval(reentrantLockScript, []string{key}, token, timeout.Seconds()).SetVal("OK")
332332

333333
var wg sync.WaitGroup
334334
wg.Add(2)
@@ -375,8 +375,8 @@ func TestAutoRenew(t *testing.T) {
375375
key := "test_key_TestAutoRenew"
376376
token := "some_token"
377377

378-
mock.ExpectEval(lockScript, []string{key}, token, lockTime.Seconds()).SetVal("OK")
379-
mock.ExpectEval(renewScript, []string{key}, token, lockTime.Seconds()).SetVal("OK")
378+
mock.ExpectEval(reentrantLockScript, []string{key}, token, lockTime.Seconds()).SetVal("OK")
379+
mock.ExpectEval(reentrantRenewScript, []string{key}, token, lockTime.Seconds()).SetVal("OK")
380380
mock.ExpectExpire(key, lockTime).SetVal(true)
381381

382382
lock := New(ctx, db, key, WithToken(token), WithAutoRenew())

0 commit comments

Comments
 (0)