fix bug of lock script and refresh script in redission rw lock
This commit is contained in:
parent
7e3d94db4b
commit
d404dc4335
|
|
@ -81,7 +81,7 @@ func NewRedisResult(res RedisCode, lockType RedisLockType, redisMsg string) erro
|
||||||
case -9:
|
case -9:
|
||||||
return &RedisResult{Code: res, Message: "redis lock failure,the lock is already occupied by another processes lock"}
|
return &RedisResult{Code: res, Message: "redis lock failure,the lock is already occupied by another processes lock"}
|
||||||
case -99:
|
case -99:
|
||||||
return &RedisResult{Code: res, Message: "redis internal execution error"}
|
return &RedisResult{Code: res, Message: fmt.Sprintf("redis internal execution error:%v\n", redisMsg)}
|
||||||
default:
|
default:
|
||||||
msg := "unkown redis execution result"
|
msg := "unkown redis execution result"
|
||||||
if redisMsg != "" {
|
if redisMsg != "" {
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ local lockKey = KEYS[2] .. ':' .. ARGV[2];
|
||||||
if (mode == false) then
|
if (mode == false) then
|
||||||
redis.call('hset', KEYS[1], 'mode', 'read');
|
redis.call('hset', KEYS[1], 'mode', 'read');
|
||||||
redis.call('hset', KEYS[1], lockKey, '1');
|
redis.call('hset', KEYS[1], lockKey, '1');
|
||||||
redis.call('hexpire', KEYS[1], ARGV[1] 'fields' '1' lockKey);
|
redis.call('hexpire', KEYS[1], ARGV[1], 'fields', '1', lockKey);
|
||||||
redis.call('expire', KEYS[1], ARGV[1]);
|
redis.call('expire', KEYS[1], ARGV[1]);
|
||||||
return 1;
|
return 1;
|
||||||
end;
|
end;
|
||||||
|
|
@ -30,10 +30,10 @@ if (mode == 'read') then
|
||||||
if (redis.call('exists', KEYS[1], ARGV[2]) == 1) then
|
if (redis.call('exists', KEYS[1], ARGV[2]) == 1) then
|
||||||
redis.call('hincrby', KEYS[1], lockKey, '1');
|
redis.call('hincrby', KEYS[1], lockKey, '1');
|
||||||
local remainTime = redis.call('httl', KEYS[1], 'fields', '1', lockKey);
|
local remainTime = redis.call('httl', KEYS[1], 'fields', '1', lockKey);
|
||||||
redis.call('hexpire', key, math.max(remainTime, ARGV[1]));
|
redis.call('hexpire', KEYS[1], math.max(remainTime, ARGV[1]), 'fields', '1', lockKey);
|
||||||
else
|
else
|
||||||
redis.call('hset', KEYS[1], lockKey, '1');
|
redis.call('hset', KEYS[1], lockKey, '1');
|
||||||
redis.call('hexpire', KEYS[1], lockKey, ARGV[1]);
|
redis.call('hexpire', KEYS[1], ARGV[1], 'fields', '1', lockKey);
|
||||||
end;
|
end;
|
||||||
local cursor = 0;
|
local cursor = 0;
|
||||||
local maxRemainTime = tonumber(ARGV[1]);
|
local maxRemainTime = tonumber(ARGV[1]);
|
||||||
|
|
@ -142,7 +142,7 @@ if (mode == false) then
|
||||||
end;
|
end;
|
||||||
redis.call('hset', KEYS[1], 'mode', 'write');
|
redis.call('hset', KEYS[1], 'mode', 'write');
|
||||||
redis.call('hset', KEYS[1], lockKey, 1);
|
redis.call('hset', KEYS[1], lockKey, 1);
|
||||||
redis.call('hexpire', KEYS[1], ARGV[1] 'fields' '1' lockKey);
|
redis.call('hexpire', KEYS[1], ARGV[1], 'fields', '1', lockKey);
|
||||||
redis.call('expire', KEYS[1], ARGV[1]);
|
redis.call('expire', KEYS[1], ARGV[1]);
|
||||||
redis.call('lpop', waitKey, '1')
|
redis.call('lpop', waitKey, '1')
|
||||||
return 1;
|
return 1;
|
||||||
|
|
@ -156,7 +156,7 @@ else
|
||||||
local lockExists = redis.call('hexists', KEYS[1], lockKey)
|
local lockExists = redis.call('hexists', KEYS[1], lockKey)
|
||||||
if (lockExists == 1) then
|
if (lockExists == 1) then
|
||||||
redis.call('hincrby', KEYS[1], lockKey, 1);
|
redis.call('hincrby', KEYS[1], lockKey, 1);
|
||||||
redis.call('hexpire', KEYS[1], ARGV[1] 'fields' '1' lockKey);
|
redis.call('hexpire', KEYS[1], ARGV[1], 'fields', '1', lockKey);
|
||||||
redis.call('expire', KEYS[1], ARGV[1]);
|
redis.call('expire', KEYS[1], ARGV[1]);
|
||||||
return 1;
|
return 1;
|
||||||
end;
|
end;
|
||||||
|
|
@ -219,11 +219,11 @@ var RefreshRWLockScript = `
|
||||||
local lockKey = KEYS[2] .. ':' .. ARGV[2]
|
local lockKey = KEYS[2] .. ':' .. ARGV[2]
|
||||||
local lockExists = redis.call('hexists', KEYS[1], lockKey);
|
local lockExists = redis.call('hexists', KEYS[1], lockKey);
|
||||||
local mode = redis.call('hget', KEYS[1], 'mode')
|
local mode = redis.call('hget', KEYS[1], 'mode')
|
||||||
|
local maxRemainTime = tonumber(ARGV[1]);
|
||||||
if (lockExists == 1) then
|
if (lockExists == 1) then
|
||||||
redis.call('hexpire', KEYS[1], ARGV[1] 'fields' '1' lockKey);
|
redis.call('hexpire', KEYS[1], ARGV[1], 'fields', '1', lockKey);
|
||||||
if (mode == 'read' ) then
|
if (mode == 'read' ) then
|
||||||
local cursor = 0;
|
local cursor = 0;
|
||||||
local maxRemainTime = tonumber(ARGV[1]);
|
|
||||||
local pattern = KEYS[2] .. ':*';
|
local pattern = KEYS[2] .. ':*';
|
||||||
repeat
|
repeat
|
||||||
local hscanResult = redis.call('hscan', KEYS[1], cursor, 'match', pattern, 'count', '100');
|
local hscanResult = redis.call('hscan', KEYS[1], cursor, 'match', pattern, 'count', '100');
|
||||||
|
|
@ -236,11 +236,15 @@ if (lockExists == 1) then
|
||||||
maxRemainTime = math.max(tonumber(remainTime[1]), maxRemainTime);
|
maxRemainTime = math.max(tonumber(remainTime[1]), maxRemainTime);
|
||||||
end;
|
end;
|
||||||
until cursor == 0;
|
until cursor == 0;
|
||||||
|
|
||||||
if (maxRemainTime > 0) then
|
if (maxRemainTime > 0) then
|
||||||
local remainTime = redis.call('ttl', KEYS[1]);
|
local remainTime = redis.call('ttl', KEYS[1]);
|
||||||
redis.call('expire', KEYS[1], math.max(tonumber(remainTime),maxRemainTime));
|
redis.call('expire', KEYS[1], math.max(tonumber(remainTime),maxRemainTime));
|
||||||
end;
|
end;
|
||||||
|
elseif (mode == 'write') then
|
||||||
|
redis.call('expire', KEYS[1], ARGV[1]);
|
||||||
end;
|
end;
|
||||||
|
-- return redis.call('ttl',KEYS[1]);
|
||||||
return 1;
|
return 1;
|
||||||
end;
|
end;
|
||||||
return -8;
|
return -8;
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,9 @@ const (
|
||||||
unlockMessage = 0
|
unlockMessage = 0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// RedissionLockConfig define redission lock config
|
||||||
type RedissionLockConfig struct {
|
type RedissionLockConfig struct {
|
||||||
LockLeaseTime time.Duration
|
LockLeaseTime uint64
|
||||||
Prefix string
|
Prefix string
|
||||||
ChanPrefix string
|
ChanPrefix string
|
||||||
TimeoutPrefix string
|
TimeoutPrefix string
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"modelRT/distributedlock/constant"
|
"modelRT/distributedlock/constant"
|
||||||
|
|
@ -107,8 +108,8 @@ func (rl *RedissionRWLocker) refreshLockTimeout() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if constant.RedisCode(val) == constant.RefreshLockFailure {
|
if constant.RedisCode(val) == constant.RefreshLockFailure {
|
||||||
rl.logger.Error("lock refreash failed,can not find the read lock by key and token", zap.String("token", rl.token), zap.String("key", rl.key))
|
rl.logger.Error("lock refreash failed,can not find the read lock by key and token", zap.String("rwTokenPrefix", rl.rwTokenTimeoutPrefix), zap.String("token", rl.token), zap.String("key", rl.key))
|
||||||
break
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if constant.RedisCode(val) == constant.RefreshLockSuccess {
|
if constant.RedisCode(val) == constant.RefreshLockSuccess {
|
||||||
|
|
@ -241,6 +242,7 @@ func GetRWLocker(client *redis.Client, ops *RedissionLockConfig) *RedissionRWLoc
|
||||||
needRefresh: true,
|
needRefresh: true,
|
||||||
client: client,
|
client: client,
|
||||||
exit: make(chan struct{}),
|
exit: make(chan struct{}),
|
||||||
|
once: &sync.Once{},
|
||||||
logger: logger.GetLoggerInstance(),
|
logger: logger.GetLoggerInstance(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -257,10 +259,11 @@ func GetRWLocker(client *redis.Client, ops *RedissionLockConfig) *RedissionRWLoc
|
||||||
}
|
}
|
||||||
|
|
||||||
if ops.LockLeaseTime == 0 {
|
if ops.LockLeaseTime == 0 {
|
||||||
r.lockLeaseTime = internalLockLeaseTime
|
ops.LockLeaseTime = internalLockLeaseTime
|
||||||
}
|
}
|
||||||
|
|
||||||
r.key = strings.Join([]string{ops.Prefix, ops.Key}, ":")
|
r.key = strings.Join([]string{ops.Prefix, ops.Key}, ":")
|
||||||
|
r.lockLeaseTime = ops.LockLeaseTime
|
||||||
r.waitChanKey = strings.Join([]string{ops.ChanPrefix, ops.Key, "write"}, ":")
|
r.waitChanKey = strings.Join([]string{ops.ChanPrefix, ops.Key, "write"}, ":")
|
||||||
|
|
||||||
rwLocker := &RedissionRWLocker{
|
rwLocker := &RedissionRWLocker{
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
package distributed_lock
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-redis/redis"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
var log *zap.Logger
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
log = zap.Must(zap.NewDevelopment())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRWLockReentrantLock(t *testing.T) {
|
||||||
|
rdb := redis.NewClient(&redis.Options{
|
||||||
|
Network: "tcp",
|
||||||
|
Addr: "192.168.2.103:6379",
|
||||||
|
Password: "cnstar",
|
||||||
|
PoolSize: 50,
|
||||||
|
DialTimeout: 10 * time.Second,
|
||||||
|
})
|
||||||
|
|
||||||
|
rwLocker := GetRWLocker(rdb, &RedissionLockConfig{
|
||||||
|
LockLeaseTime: 120,
|
||||||
|
Key: "component",
|
||||||
|
})
|
||||||
|
|
||||||
|
rwLocker.logger = log
|
||||||
|
t.Logf("%+v\n", rwLocker)
|
||||||
|
|
||||||
|
duration := 10 * time.Second
|
||||||
|
// 第一次加读锁
|
||||||
|
err := rwLocker.RLock(duration)
|
||||||
|
t.Logf("err:%+v\n", err)
|
||||||
|
// TODO 实现可重入读锁测试
|
||||||
|
// rwLocker.UnRLock()
|
||||||
|
// // 第二次加读锁
|
||||||
|
// rwLocker.RLock(duration)
|
||||||
|
// // 查看 redis 中相关 key 的值
|
||||||
|
// rwLocker.UnRLock()
|
||||||
|
t.Log("test success")
|
||||||
|
select {}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue