Closed XiaoyuShi6 closed 1 year ago
Your updateExpire is broken as it returns the connection to the pool while the goroutine is still using it.
You want:
func (r *RedisLock) updateExpire(ctx context.Context) {
expireClient := redisClient.Get()
go func() {
defer expireClient.Close()
for {
select {
case <-ctx.Done():
return
default:
res, err := redis.Int(expireClient.Do("EXPIRE", r.key, r.ttl))
log.Debug("updateExpire")
if res != 1 {
if err != nil {
log.Error("update redis lock expire error: %s,key : %s", err.Error(), r.key)
} else {
log.Error("update redis lock expire error,key : %s", r.key)
}
}
}
time.Sleep(time.Duration(r.ttl/2) * time.Second)
}
}()
}
Hi, I guess I meet the same concurrency bug as https://github.com/gomodule/redigo/issues/540. This is the panic log:
I want to use redigo to realize a distributed lock.After get the lock, create a new goroutine to update the expire time. Everywhere I use redis, I will get a new client from the redisPool and close it after using. However, I still meet this problem.Here is my code:
func (r *RedisLock) grant() (err error) { grantClient := redisClient.Get() defer grantClient.Close() res, err := redis.String(grantClient.Do("SET", r.key, r.lockId, "EX", r.ttl, "NX")) if res != "OK" { err = errors.New(fmt.Sprintf("grant res is not OK: %s", res)) return err }
}
func (r *RedisLock) updateExpire(ctx context.Context) { expireClient := redisClient.Get() defer expireClient.Close() go func() { for { select { case <-ctx.Done(): return default: res, err := redis.Int(expireClient.Do("EXPIRE", r.key, r.ttl)) log.Debug("updateExpire") if res != 1 { if err != nil { log.Error("update redis lock expire error: %s,key : %s", err.Error(), r.key) } else { log.Error("update redis lock expire error,key : %s", r.key) } }
}
func (r *RedisLock) UnLock(lockId string) (err error) { unlockClient := redisClient.Get() defer unlockClient.Close() if r.cancelFun != nil { r.cancelFun() } if r.isLocked { if lockId != r.lockId { err = errors.New(fmt.Sprintf("lockid is different, can't unlock, key: %s", r.key)) } else { res, _ := redis.Int(unlockClient.Do("DEL", r.key)) if res != 1 { return errors.New(fmt.Sprintf("redis unlock error,key: %s", r.key)) } } } return }