refactor(locker script): optimize of redis locker script

1.optimize locker script of un read lock
fix(locker_refresh): fix bug of locker refresh
    1.fix bug of locker refresh when locker lock success after wait
test(lock_case): add new redis locker case
    1.add new redis locker case
This commit is contained in:
douxu 2025-04-11 16:36:54 +08:00
parent fda43c65d2
commit d27a9bbafa
4 changed files with 44 additions and 9 deletions

View File

@ -79,10 +79,10 @@ elseif (mode == 'write') then
return -2; return -2;
end; end;
-- 判断当前的确是读模式但是当前 token 并没有加读锁的情况返回 1 -- 判断当前的确是读模式但是当前 token 并没有加读锁的情况返回 0
local lockExists = redis.call('hexists', KEYS[1], lockKey); local lockExists = redis.call('hexists', KEYS[1], lockKey);
if ((mode == 'read') and (lockExists == 0)) then if ((mode == 'read') and (lockExists == 0)) then
return 1; return 0;
end; end;
local counter = redis.call('hincrby', KEYS[1], lockKey, -1); local counter = redis.call('hincrby', KEYS[1], lockKey, -1);

View File

@ -167,7 +167,10 @@ func (rl *redissionLocker) cancelRefreshLockTime() {
func (rl *redissionLocker) closeSub(sub *redis.PubSub, noticeChan chan struct{}) { func (rl *redissionLocker) closeSub(sub *redis.PubSub, noticeChan chan struct{}) {
if sub != nil { if sub != nil {
sub.Close() err := sub.Close()
if err != nil {
rl.logger.Error("close sub failed", zap.String("token", rl.token), zap.String("key", rl.key), zap.Error(err))
}
} }
if noticeChan != nil { if noticeChan != nil {

View File

@ -74,6 +74,17 @@ func (rl *RedissionRWLocker) RLock(ctx context.Context, timeout ...time.Duration
if result.Code == constant.LockSuccess { if result.Code == constant.LockSuccess {
rl.logger.Info(result.OutputResultMessage()) rl.logger.Info(result.OutputResultMessage())
rl.closeSub(sub, rl.subExitChan) rl.closeSub(sub, rl.subExitChan)
if rl.needRefresh {
rl.refreshOnce.Do(func() {
if rl.refreshExitChan == nil {
rl.refreshExitChan = make(chan struct{})
}
// async refresh lock timeout unitl receive exit singal
go rl.refreshLockTimeout(ctx)
})
}
return nil return nil
} }
case <-acquireTimer.C: case <-acquireTimer.C:
@ -206,6 +217,17 @@ func (rl *RedissionRWLocker) WLock(ctx context.Context, timeout ...time.Duration
if result.Code == constant.LockSuccess { if result.Code == constant.LockSuccess {
rl.logger.Info(result.OutputResultMessage()) rl.logger.Info(result.OutputResultMessage())
rl.closeSub(sub, rl.subExitChan) rl.closeSub(sub, rl.subExitChan)
if rl.needRefresh {
rl.refreshOnce.Do(func() {
if rl.refreshExitChan == nil {
rl.refreshExitChan = make(chan struct{})
}
// async refresh lock timeout unitl receive exit singal
go rl.refreshLockTimeout(ctx)
})
}
return nil return nil
} }
case <-acquireTimer.C: case <-acquireTimer.C:

View File

@ -526,7 +526,6 @@ func TestRWLock2CWithRLockAndWLockSucceed(t *testing.T) {
return return
} }
// TODO 设计两个客户端,C1先加写锁与C2后加读锁
func TestRWLock2CWithWLockAndRLock(t *testing.T) { func TestRWLock2CWithWLockAndRLock(t *testing.T) {
ctx := context.TODO() ctx := context.TODO()
@ -547,7 +546,7 @@ func TestRWLock2CWithWLockAndRLock(t *testing.T) {
rwLocker2.logger = log rwLocker2.logger = log
duration := 10 * time.Second duration := 10 * time.Second
// locker1加 // locker1加
err := rwLocker1.WLock(ctx, duration) err := rwLocker1.WLock(ctx, duration)
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
@ -556,14 +555,25 @@ func TestRWLock2CWithWLockAndRLock(t *testing.T) {
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
assert.Equal(t, 1, num) assert.Equal(t, 1, num)
go func() {
// locker1解写锁
time.Sleep(10 * time.Second)
err = rwLocker1.UnWLock(ctx)
assert.Equal(t, nil, err)
}()
// locker2加读锁 // locker2加读锁
duration = 2 * time.Second duration = 30 * time.Second
err = rwLocker2.RLock(ctx, duration) err = rwLocker2.RLock(ctx, duration)
// TODO 预测加读锁失败
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
// locker1解写锁 tokenKey2 := strings.Join([]string{rwLocker2.rwTokenTimeoutPrefix, rwLocker2.token}, ":")
err = rwLocker1.UnWLock(ctx) num, err = rdb.HGet(ctx, rwLocker2.key, tokenKey2).Int()
assert.Equal(t, nil, err)
assert.Equal(t, 1, num)
// locker2解读锁
err = rwLocker2.UnRLock(ctx)
assert.Equal(t, nil, err) assert.Equal(t, nil, err)
t.Log("rwLock 2 client lock test success") t.Log("rwLock 2 client lock test success")