|
|
@ -4,10 +4,20 @@ import ( |
|
|
|
"context" |
|
|
|
"context" |
|
|
|
"crypto/rand" |
|
|
|
"crypto/rand" |
|
|
|
"encoding/base64" |
|
|
|
"encoding/base64" |
|
|
|
|
|
|
|
"errors" |
|
|
|
|
|
|
|
|
|
|
|
"github.com/go-redis/redis/v8" |
|
|
|
"github.com/go-redis/redis/v8" |
|
|
|
) |
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type LockResult int |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const ( |
|
|
|
|
|
|
|
_ LockResult = iota |
|
|
|
|
|
|
|
LockResultSuccess |
|
|
|
|
|
|
|
LockResultRenewalSuccess |
|
|
|
|
|
|
|
LockResultFail |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
type RedisPreempt struct { |
|
|
|
type RedisPreempt struct { |
|
|
|
key string |
|
|
|
key string |
|
|
|
password string |
|
|
|
password string |
|
|
@ -31,10 +41,15 @@ func (p *RedisPreempt) init() { |
|
|
|
_, _ = rand.Read(byt) |
|
|
|
_, _ = rand.Read(byt) |
|
|
|
p.password = base64.StdEncoding.EncodeToString(byt) |
|
|
|
p.password = base64.StdEncoding.EncodeToString(byt) |
|
|
|
p.lockScript = redis.NewScript(` |
|
|
|
p.lockScript = redis.NewScript(` |
|
|
|
if redis.call('get',KEYS[1]) == ARGV[1] then |
|
|
|
local val = redis.call('get', KEYS[1]) |
|
|
|
return redis.call('expire', KEYS[1], ARGV[2]) |
|
|
|
if (val==nil or (type(val) == "boolean" and not val)) then |
|
|
|
|
|
|
|
redis.call('set', KEYS[1], ARGV[1], 'ex', ARGV[2]) |
|
|
|
|
|
|
|
return 'LockResultSuccess' |
|
|
|
|
|
|
|
elseif (val == ARGV[1]) then |
|
|
|
|
|
|
|
redis.call('expire', KEYS[1], ARGV[2]) |
|
|
|
|
|
|
|
return 'LockResultRenewalSuccess' |
|
|
|
else
|
|
|
|
else
|
|
|
|
return redis.call('set', KEYS[1], ARGV[1], 'ex', ARGV[2], 'nx') |
|
|
|
return 'LockResultFail' |
|
|
|
end |
|
|
|
end |
|
|
|
`) |
|
|
|
`) |
|
|
|
p.unlockScript = redis.NewScript(` |
|
|
|
p.unlockScript = redis.NewScript(` |
|
|
@ -47,10 +62,23 @@ func (p *RedisPreempt) init() { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// TryLock attempt to lock the master for ttlSecond
|
|
|
|
// TryLock attempt to lock the master for ttlSecond
|
|
|
|
func (p *RedisPreempt) TryLock(ttlSecond int) (ok bool, err error) { |
|
|
|
func (p *RedisPreempt) TryLock(ttlSecond int) (result LockResult, err error) { |
|
|
|
ok, err = p.lockScript.Run(context.Background(), redisInstance, []string{p.key}, p.password, ttlSecond).Bool() |
|
|
|
ret, err := p.lockScript.Run(context.Background(), redisInstance, []string{p.key}, p.password, ttlSecond).Text() |
|
|
|
p.lastLockStatus = ok |
|
|
|
if err != nil { |
|
|
|
return |
|
|
|
return LockResultFail, err |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
switch ret { |
|
|
|
|
|
|
|
case "LockResultRenewalSuccess": |
|
|
|
|
|
|
|
p.lastLockStatus = true |
|
|
|
|
|
|
|
return LockResultRenewalSuccess, nil |
|
|
|
|
|
|
|
case "LockResultSuccess": |
|
|
|
|
|
|
|
p.lastLockStatus = true |
|
|
|
|
|
|
|
return LockResultSuccess, nil |
|
|
|
|
|
|
|
case "LockResultFail": |
|
|
|
|
|
|
|
return LockResultFail, nil |
|
|
|
|
|
|
|
default: |
|
|
|
|
|
|
|
return LockResultFail, errors.New("fixme: unknown return") |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Unlock try to release the master permission
|
|
|
|
// Unlock try to release the master permission
|
|
|
|