blob: 9fe1376bbd96a9e971bf1e5bda9fa8a063828812 [file] [log] [blame]
Joey Armstronge8c091f2023-01-17 16:56:26 -05001package redis
2
3import (
4 "context"
5 "io"
6 "net"
7 "strings"
8
9 "github.com/go-redis/redis/v8/internal/pool"
10 "github.com/go-redis/redis/v8/internal/proto"
11)
12
13var ErrClosed = pool.ErrClosed
14
15type Error interface {
16 error
17
18 // RedisError is a no-op function but
19 // serves to distinguish types that are Redis
20 // errors from ordinary errors: a type is a
21 // Redis error if it has a RedisError method.
22 RedisError()
23}
24
25var _ Error = proto.RedisError("")
26
27func shouldRetry(err error, retryTimeout bool) bool {
28 switch err {
29 case io.EOF, io.ErrUnexpectedEOF:
30 return true
31 case nil, context.Canceled, context.DeadlineExceeded:
32 return false
33 }
34
35 if v, ok := err.(timeoutError); ok {
36 if v.Timeout() {
37 return retryTimeout
38 }
39 return true
40 }
41
42 s := err.Error()
43 if s == "ERR max number of clients reached" {
44 return true
45 }
46 if strings.HasPrefix(s, "LOADING ") {
47 return true
48 }
49 if strings.HasPrefix(s, "READONLY ") {
50 return true
51 }
52 if strings.HasPrefix(s, "CLUSTERDOWN ") {
53 return true
54 }
55 if strings.HasPrefix(s, "TRYAGAIN ") {
56 return true
57 }
58
59 return false
60}
61
62func isRedisError(err error) bool {
63 _, ok := err.(proto.RedisError)
64 return ok
65}
66
67func isBadConn(err error, allowTimeout bool) bool {
68 if err == nil {
69 return false
70 }
71
72 if isRedisError(err) {
73 // Close connections in read only state in case domain addr is used
74 // and domain resolves to a different Redis Server. See #790.
75 return isReadOnlyError(err)
76 }
77
78 if allowTimeout {
79 if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
80 return !netErr.Temporary()
81 }
82 }
83
84 return true
85}
86
87func isMovedError(err error) (moved bool, ask bool, addr string) {
88 if !isRedisError(err) {
89 return
90 }
91
92 s := err.Error()
93 switch {
94 case strings.HasPrefix(s, "MOVED "):
95 moved = true
96 case strings.HasPrefix(s, "ASK "):
97 ask = true
98 default:
99 return
100 }
101
102 ind := strings.LastIndex(s, " ")
103 if ind == -1 {
104 return false, false, ""
105 }
106 addr = s[ind+1:]
107 return
108}
109
110func isLoadingError(err error) bool {
111 return strings.HasPrefix(err.Error(), "LOADING ")
112}
113
114func isReadOnlyError(err error) bool {
115 return strings.HasPrefix(err.Error(), "READONLY ")
116}
117
118//------------------------------------------------------------------------------
119
120type timeoutError interface {
121 Timeout() bool
122}