forked from ebhomengo/niki
1
0
Fork 0
niki/vendor/github.com/redis/go-redis/v9/error.go

253 lines
3.2 KiB
Go
Raw Normal View History

2024-02-18 10:42:21 +00:00
package redis
import (
"context"
"errors"
"io"
"net"
"strings"
"github.com/redis/go-redis/v9/internal/pool"
"github.com/redis/go-redis/v9/internal/proto"
)
// ErrClosed performs any operation on the closed client will return this error.
2024-02-18 10:42:21 +00:00
var ErrClosed = pool.ErrClosed
// HasErrorPrefix checks if the err is a Redis error and the message contains a prefix.
2024-02-18 10:42:21 +00:00
func HasErrorPrefix(err error, prefix string) bool {
2024-02-18 10:42:21 +00:00
var rErr Error
2024-02-18 10:42:21 +00:00
if !errors.As(err, &rErr) {
2024-02-18 10:42:21 +00:00
return false
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
msg := rErr.Error()
2024-02-18 10:42:21 +00:00
msg = strings.TrimPrefix(msg, "ERR ") // KVRocks adds such prefix
2024-02-18 10:42:21 +00:00
return strings.HasPrefix(msg, prefix)
2024-02-18 10:42:21 +00:00
}
type Error interface {
error
// RedisError is a no-op function but
2024-02-18 10:42:21 +00:00
// serves to distinguish types that are Redis
2024-02-18 10:42:21 +00:00
// errors from ordinary errors: a type is a
2024-02-18 10:42:21 +00:00
// Redis error if it has a RedisError method.
2024-02-18 10:42:21 +00:00
RedisError()
}
var _ Error = proto.RedisError("")
func shouldRetry(err error, retryTimeout bool) bool {
2024-02-18 10:42:21 +00:00
switch err {
2024-02-18 10:42:21 +00:00
case io.EOF, io.ErrUnexpectedEOF:
2024-02-18 10:42:21 +00:00
return true
2024-02-18 10:42:21 +00:00
case nil, context.Canceled, context.DeadlineExceeded:
2024-02-18 10:42:21 +00:00
return false
2024-02-18 10:42:21 +00:00
}
if v, ok := err.(timeoutError); ok {
2024-02-18 10:42:21 +00:00
if v.Timeout() {
2024-02-18 10:42:21 +00:00
return retryTimeout
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return true
2024-02-18 10:42:21 +00:00
}
s := err.Error()
2024-02-18 10:42:21 +00:00
if s == "ERR max number of clients reached" {
2024-02-18 10:42:21 +00:00
return true
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if strings.HasPrefix(s, "LOADING ") {
2024-02-18 10:42:21 +00:00
return true
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if strings.HasPrefix(s, "READONLY ") {
2024-02-18 10:42:21 +00:00
return true
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if strings.HasPrefix(s, "CLUSTERDOWN ") {
2024-02-18 10:42:21 +00:00
return true
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if strings.HasPrefix(s, "TRYAGAIN ") {
2024-02-18 10:42:21 +00:00
return true
2024-02-18 10:42:21 +00:00
}
return false
2024-02-18 10:42:21 +00:00
}
func isRedisError(err error) bool {
2024-02-18 10:42:21 +00:00
_, ok := err.(proto.RedisError)
2024-02-18 10:42:21 +00:00
return ok
2024-02-18 10:42:21 +00:00
}
func isBadConn(err error, allowTimeout bool, addr string) bool {
2024-02-18 10:42:21 +00:00
switch err {
2024-02-18 10:42:21 +00:00
case nil:
2024-02-18 10:42:21 +00:00
return false
2024-02-18 10:42:21 +00:00
case context.Canceled, context.DeadlineExceeded:
2024-02-18 10:42:21 +00:00
return true
2024-02-18 10:42:21 +00:00
}
if isRedisError(err) {
2024-02-18 10:42:21 +00:00
switch {
2024-02-18 10:42:21 +00:00
case isReadOnlyError(err):
2024-02-18 10:42:21 +00:00
// Close connections in read only state in case domain addr is used
2024-02-18 10:42:21 +00:00
// and domain resolves to a different Redis Server. See #790.
2024-02-18 10:42:21 +00:00
return true
2024-02-18 10:42:21 +00:00
case isMovedSameConnAddr(err, addr):
2024-02-18 10:42:21 +00:00
// Close connections when we are asked to move to the same addr
2024-02-18 10:42:21 +00:00
// of the connection. Force a DNS resolution when all connections
2024-02-18 10:42:21 +00:00
// of the pool are recycled
2024-02-18 10:42:21 +00:00
return true
2024-02-18 10:42:21 +00:00
default:
2024-02-18 10:42:21 +00:00
return false
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
if allowTimeout {
2024-02-18 10:42:21 +00:00
if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
2024-02-18 10:42:21 +00:00
return false
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
return true
2024-02-18 10:42:21 +00:00
}
func isMovedError(err error) (moved bool, ask bool, addr string) {
2024-02-18 10:42:21 +00:00
if !isRedisError(err) {
2024-02-18 10:42:21 +00:00
return
2024-02-18 10:42:21 +00:00
}
s := err.Error()
2024-02-18 10:42:21 +00:00
switch {
2024-02-18 10:42:21 +00:00
case strings.HasPrefix(s, "MOVED "):
2024-02-18 10:42:21 +00:00
moved = true
2024-02-18 10:42:21 +00:00
case strings.HasPrefix(s, "ASK "):
2024-02-18 10:42:21 +00:00
ask = true
2024-02-18 10:42:21 +00:00
default:
2024-02-18 10:42:21 +00:00
return
2024-02-18 10:42:21 +00:00
}
ind := strings.LastIndex(s, " ")
2024-02-18 10:42:21 +00:00
if ind == -1 {
2024-02-18 10:42:21 +00:00
return false, false, ""
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
addr = s[ind+1:]
2024-02-18 10:42:21 +00:00
return
2024-02-18 10:42:21 +00:00
}
func isLoadingError(err error) bool {
2024-02-18 10:42:21 +00:00
return strings.HasPrefix(err.Error(), "LOADING ")
2024-02-18 10:42:21 +00:00
}
func isReadOnlyError(err error) bool {
2024-02-18 10:42:21 +00:00
return strings.HasPrefix(err.Error(), "READONLY ")
2024-02-18 10:42:21 +00:00
}
func isMovedSameConnAddr(err error, addr string) bool {
2024-02-18 10:42:21 +00:00
redisError := err.Error()
2024-02-18 10:42:21 +00:00
if !strings.HasPrefix(redisError, "MOVED ") {
2024-02-18 10:42:21 +00:00
return false
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return strings.HasSuffix(redisError, " "+addr)
2024-02-18 10:42:21 +00:00
}
//------------------------------------------------------------------------------
type timeoutError interface {
Timeout() bool
}