From 5b0d0e220afc49055652815e1cbdd5a6d9176cd1 Mon Sep 17 00:00:00 2001 From: seth-shi <1033404553@qq.com> Date: Fri, 16 Aug 2024 18:08:40 +0800 Subject: [PATCH 1/3] fix: redis locker reAcquire --- core/stores/redis/redislock_test.go | 52 +++++++++++++++++------------ 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/core/stores/redis/redislock_test.go b/core/stores/redis/redislock_test.go index 4f1d535d74eb..cab937815abc 100644 --- a/core/stores/redis/redislock_test.go +++ b/core/stores/redis/redislock_test.go @@ -35,31 +35,39 @@ func TestRedisLock(t *testing.T) { } } - t.Run("normal", func(t *testing.T) { - runOnRedis(t, testFn(nil)) - }) + t.Run( + "normal", func(t *testing.T) { + runOnRedis(t, testFn(nil)) + }, + ) - t.Run("withContext", func(t *testing.T) { - runOnRedis(t, testFn(context.Background())) - }) + t.Run( + "withContext", func(t *testing.T) { + runOnRedis(t, testFn(context.Background())) + }, + ) } func TestRedisLock_Expired(t *testing.T) { - runOnRedis(t, func(client *Redis) { - key := stringx.Rand() - redisLock := NewRedisLock(client, key) - ctx, cancel := context.WithCancel(context.Background()) - cancel() - _, err := redisLock.AcquireCtx(ctx) - assert.NotNil(t, err) - }) + runOnRedis( + t, func(client *Redis) { + key := stringx.Rand() + redisLock := NewRedisLock(client, key) + ctx, cancel := context.WithCancel(context.Background()) + cancel() + _, err := redisLock.AcquireCtx(ctx) + assert.NotNil(t, err) + }, + ) - runOnRedis(t, func(client *Redis) { - key := stringx.Rand() - redisLock := NewRedisLock(client, key) - ctx, cancel := context.WithCancel(context.Background()) - cancel() - _, err := redisLock.ReleaseCtx(ctx) - assert.NotNil(t, err) - }) + runOnRedis( + t, func(client *Redis) { + key := stringx.Rand() + redisLock := NewRedisLock(client, key) + ctx, cancel := context.WithCancel(context.Background()) + cancel() + _, err := redisLock.ReleaseCtx(ctx) + assert.NotNil(t, err) + }, + ) } From 2d7ad75c824117c4415ecb15f90efd7739ac39fd Mon Sep 17 00:00:00 2001 From: seth-shi <1033404553@qq.com> Date: Fri, 16 Aug 2024 18:12:16 +0800 Subject: [PATCH 2/3] fix: locker --- core/stores/redis/redislock.go | 43 ++++++++++++----------------- core/stores/redis/redislock_test.go | 33 ++++++++++++++++++++++ 2 files changed, 51 insertions(+), 25 deletions(-) diff --git a/core/stores/redis/redislock.go b/core/stores/redis/redislock.go index c740ef3689bd..04b3c80b66f4 100644 --- a/core/stores/redis/redislock.go +++ b/core/stores/redis/redislock.go @@ -3,7 +3,6 @@ package redis import ( "context" "math/rand" - "strconv" "sync/atomic" "time" @@ -14,23 +13,17 @@ import ( ) const ( - randomLen = 16 - tolerance = 500 // milliseconds - millisPerSecond = 1000 + randomLen = 16 ) var ( - lockScript = NewScript(`if redis.call("GET", KEYS[1]) == ARGV[1] then - redis.call("SET", KEYS[1], ARGV[1], "PX", ARGV[2]) - return "OK" -else - return redis.call("SET", KEYS[1], ARGV[1], "NX", "PX", ARGV[2]) -end`) - delScript = NewScript(`if redis.call("GET", KEYS[1]) == ARGV[1] then + delScript = NewScript( + `if redis.call("GET", KEYS[1]) == ARGV[1] then return redis.call("DEL", KEYS[1]) else return 0 -end`) +end`, + ) ) // A RedisLock is a redis lock. @@ -61,26 +54,26 @@ func (rl *RedisLock) Acquire() (bool, error) { // AcquireCtx acquires the lock with the given ctx. func (rl *RedisLock) AcquireCtx(ctx context.Context) (bool, error) { - seconds := atomic.LoadUint32(&rl.seconds) - resp, err := rl.store.ScriptRunCtx(ctx, lockScript, []string{rl.key}, []string{ - rl.id, strconv.Itoa(int(seconds)*millisPerSecond + tolerance), - }) + var ( + seconds = atomic.LoadUint32(&rl.seconds) + res bool + err error + ) + + if seconds == 0 { + res, err = rl.store.SetnxCtx(ctx, rl.key, rl.id) + } else { + res, err = rl.store.SetnxExCtx(ctx, rl.key, rl.id, int(seconds)) + } + if err == red.Nil { return false, nil } else if err != nil { logx.Errorf("Error on acquiring lock for %s, %s", rl.key, err.Error()) return false, err - } else if resp == nil { - return false, nil - } - - reply, ok := resp.(string) - if ok && reply == "OK" { - return true, nil } - logx.Errorf("Unknown reply when acquiring lock for %s: %v", rl.key, resp) - return false, nil + return res, nil } // Release releases the lock. diff --git a/core/stores/redis/redislock_test.go b/core/stores/redis/redislock_test.go index cab937815abc..e8a2a4d6e45c 100644 --- a/core/stores/redis/redislock_test.go +++ b/core/stores/redis/redislock_test.go @@ -3,12 +3,45 @@ package redis import ( "context" "testing" + "time" + "github.com/alicebob/miniredis/v2" "github.com/stretchr/testify/assert" "github.com/zeromicro/go-zero/core/stringx" ) +func TestRedisLock_SameAcquire(t *testing.T) { + + var ( + s = miniredis.RunT(t) + seconds = 5 + ) + client := MustNewRedis( + RedisConf{ + Host: s.Addr(), + Type: NodeType, + }, + ) + + key := stringx.Rand() + firstLock := NewRedisLock(client, key) + firstLock.SetExpire(seconds) + firstAcquire, err := firstLock.Acquire() + assert.Nil(t, err) + assert.True(t, firstAcquire) + + secondAcquire, err := firstLock.Acquire() + assert.Nil(t, err) + assert.False(t, secondAcquire) + + s.FastForward(time.Second * time.Duration(seconds+1)) + + thirdAcquire, err := firstLock.Acquire() + assert.Nil(t, err) + assert.True(t, thirdAcquire) +} + func TestRedisLock(t *testing.T) { testFn := func(ctx context.Context) func(client *Redis) { return func(client *Redis) { From ebaab967c32e3874d3dde888c081f6b5c9ef0c9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=BD=E5=9B=BD=E9=B9=8F?= <1033404553@qq.com> Date: Mon, 19 Aug 2024 15:51:50 +0000 Subject: [PATCH 3/3] fix: redis del lua script --- core/stores/redis/lockscript.lua | 6 ------ core/stores/redis/redislock.go | 2 ++ 2 files changed, 2 insertions(+), 6 deletions(-) delete mode 100644 core/stores/redis/lockscript.lua diff --git a/core/stores/redis/lockscript.lua b/core/stores/redis/lockscript.lua deleted file mode 100644 index 11a1fe350afb..000000000000 --- a/core/stores/redis/lockscript.lua +++ /dev/null @@ -1,6 +0,0 @@ -if redis.call("GET", KEYS[1]) == ARGV[1] then - redis.call("SET", KEYS[1], ARGV[1], "PX", ARGV[2]) - return "OK" -else - return redis.call("SET", KEYS[1], ARGV[1], "NX", "PX", ARGV[2]) -end \ No newline at end of file diff --git a/core/stores/redis/redislock.go b/core/stores/redis/redislock.go index c6a489b50c4e..93b136d78b7c 100644 --- a/core/stores/redis/redislock.go +++ b/core/stores/redis/redislock.go @@ -18,6 +18,8 @@ const ( ) var ( + //go:embed delscript.lua + delLuaScript string delScript = NewScript(delLuaScript) )