Hi, I guess I meet the same concurrency bug as https://github.com/gomodule/redigo/issues/540. This is the panic log:
panic: runtime error: slice bounds out of range [15:10]
goroutine 16 [running]:
bufio.(*Reader).ReadSlice(0xc00007b200, 0x8a?)
/home/work/go_install/go/src/bufio/bufio.go:346 +0x225
github.com/gomodule/redigo/redis.(*conn).readLine(0xc0004dbac0?)
/home/work/go/pkg/mod/github.com/gomodule/[email protected]+incompatible/redis/conn.go:431 +0x26
github.com/gomodule/redigo/redis.(*conn).readReply(0xc00058e8c0)
/home/work/go/pkg/mod/github.com/gomodule/[email protected]+incompatible/redis/conn.go:504 +0x25
github.com/gomodule/redigo/redis.(*conn).DoWithTimeout(0xc00058e8c0, 0x0, {0xba10eb, 0x3}, {0xc0005888c0, 0x5, 0x5})
/home/work/go/pkg/mod/github.com/gomodule/[email protected]+incompatible/redis/conn.go:665 +0x3cf
github.com/gomodule/redigo/redis.(*conn).Do(0xaee440?, {0xba10eb?, 0xba10eb?}, {0xc0005888c0?, 0x10?, 0xabf780?})
/home/work/go/pkg/mod/github.com/gomodule/[email protected]+incompatible/redis/conn.go:616 +0x39
github.com/gomodule/redigo/redis.(*activeConn).Do(0xc000010558, {0xba10eb, 0x3}, {0xc0005888c0, 0x5, 0x5})
/home/work/go/pkg/mod/github.com/gomodule/[email protected]+incompatible/redis/pool.go:447 +0xea
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:
lock
func (r *RedisLock) Lock() (err error) {
for {
if err = r.grant(); err != nil {
time.Sleep(100 * time.Millisecond)
} else {
break
}
}
log.Debug("redis key lock success, key: %s", r.key)
ctx, cancelFun := context.WithCancel(context.TODO())
r.cancelFun = cancelFun
r.updateExpire(ctx)
r.isLocked = true
return nil
}
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
}
return nil
}
update expire
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)
}
}
}
time.Sleep(time.Duration(r.ttl/2) * time.Second)
}
}()
}
unlock
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
}
Thanks !