| package redis |
| |
| import ( |
| "context" |
| "io" |
| "net" |
| "strings" |
| |
| "github.com/go-redis/redis/v8/internal/pool" |
| "github.com/go-redis/redis/v8/internal/proto" |
| ) |
| |
| var ErrClosed = pool.ErrClosed |
| |
| type Error interface { |
| error |
| |
| // RedisError is a no-op function but |
| // serves to distinguish types that are Redis |
| // errors from ordinary errors: a type is a |
| // Redis error if it has a RedisError method. |
| RedisError() |
| } |
| |
| var _ Error = proto.RedisError("") |
| |
| func shouldRetry(err error, retryTimeout bool) bool { |
| switch err { |
| case io.EOF, io.ErrUnexpectedEOF: |
| return true |
| case nil, context.Canceled, context.DeadlineExceeded: |
| return false |
| } |
| |
| if v, ok := err.(timeoutError); ok { |
| if v.Timeout() { |
| return retryTimeout |
| } |
| return true |
| } |
| |
| s := err.Error() |
| if s == "ERR max number of clients reached" { |
| return true |
| } |
| if strings.HasPrefix(s, "LOADING ") { |
| return true |
| } |
| if strings.HasPrefix(s, "READONLY ") { |
| return true |
| } |
| if strings.HasPrefix(s, "CLUSTERDOWN ") { |
| return true |
| } |
| if strings.HasPrefix(s, "TRYAGAIN ") { |
| return true |
| } |
| |
| return false |
| } |
| |
| func isRedisError(err error) bool { |
| _, ok := err.(proto.RedisError) |
| return ok |
| } |
| |
| func isBadConn(err error, allowTimeout bool) bool { |
| if err == nil { |
| return false |
| } |
| |
| if isRedisError(err) { |
| // Close connections in read only state in case domain addr is used |
| // and domain resolves to a different Redis Server. See #790. |
| return isReadOnlyError(err) |
| } |
| |
| if allowTimeout { |
| if netErr, ok := err.(net.Error); ok && netErr.Timeout() { |
| return !netErr.Temporary() |
| } |
| } |
| |
| return true |
| } |
| |
| func isMovedError(err error) (moved bool, ask bool, addr string) { |
| if !isRedisError(err) { |
| return |
| } |
| |
| s := err.Error() |
| switch { |
| case strings.HasPrefix(s, "MOVED "): |
| moved = true |
| case strings.HasPrefix(s, "ASK "): |
| ask = true |
| default: |
| return |
| } |
| |
| ind := strings.LastIndex(s, " ") |
| if ind == -1 { |
| return false, false, "" |
| } |
| addr = s[ind+1:] |
| return |
| } |
| |
| func isLoadingError(err error) bool { |
| return strings.HasPrefix(err.Error(), "LOADING ") |
| } |
| |
| func isReadOnlyError(err error) bool { |
| return strings.HasPrefix(err.Error(), "READONLY ") |
| } |
| |
| //------------------------------------------------------------------------------ |
| |
| type timeoutError interface { |
| Timeout() bool |
| } |