diff --git a/core/blockchain_impl.go b/core/blockchain_impl.go index 28f2cd904..6e7c2ccf9 100644 --- a/core/blockchain_impl.go +++ b/core/blockchain_impl.go @@ -3186,7 +3186,13 @@ func (bc *BlockChainImpl) isInitTiKV() bool { func (bc *BlockChainImpl) tikvPreemptMaster(fromBlock, toBlock uint64) bool { for { // preempt master - if ok, _ := bc.redisPreempt.TryLock(60); ok { + lockType, _ := bc.redisPreempt.TryLock(60) + switch lockType { + case redis_helper.LockResultRenewalSuccess: + return true + case redis_helper.LockResultSuccess: + // first to master + bc.validatorListByDelegatorCache.Purge() return true } diff --git a/internal/tikv/redis_helper/lock.go b/internal/tikv/redis_helper/lock.go index 6f92b7acc..3456906b1 100644 --- a/internal/tikv/redis_helper/lock.go +++ b/internal/tikv/redis_helper/lock.go @@ -4,10 +4,20 @@ import ( "context" "crypto/rand" "encoding/base64" + "errors" "github.com/go-redis/redis/v8" ) +type LockResult int + +const ( + _ LockResult = iota + LockResultSuccess + LockResultRenewalSuccess + LockResultFail +) + type RedisPreempt struct { key string password string @@ -31,10 +41,15 @@ func (p *RedisPreempt) init() { _, _ = rand.Read(byt) p.password = base64.StdEncoding.EncodeToString(byt) p.lockScript = redis.NewScript(` - if redis.call('get',KEYS[1]) == ARGV[1] then - return redis.call('expire', KEYS[1], ARGV[2]) - else - return redis.call('set', KEYS[1], ARGV[1], 'ex', ARGV[2], 'nx') + local val = redis.call('get', KEYS[1]) + 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 + return 'LockResultFail' end `) p.unlockScript = redis.NewScript(` @@ -47,10 +62,23 @@ func (p *RedisPreempt) init() { } // TryLock attempt to lock the master for ttlSecond -func (p *RedisPreempt) TryLock(ttlSecond int) (ok bool, err error) { - ok, err = p.lockScript.Run(context.Background(), redisInstance, []string{p.key}, p.password, ttlSecond).Bool() - p.lastLockStatus = ok - return +func (p *RedisPreempt) TryLock(ttlSecond int) (result LockResult, err error) { + ret, err := p.lockScript.Run(context.Background(), redisInstance, []string{p.key}, p.password, ttlSecond).Text() + if err != nil { + 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