25'ten fazla konu seçemezsiniz Konular bir harf veya rakamla başlamalı, kısa çizgiler ('-') içerebilir ve en fazla 35 karakter uzunluğunda olabilir.

190 satır
4.2 KiB

  1. package redis
  2. import (
  3. "context"
  4. "errors"
  5. "io"
  6. "net"
  7. "strings"
  8. "github.com/redis/go-redis/v9/internal"
  9. "github.com/redis/go-redis/v9/internal/pool"
  10. "github.com/redis/go-redis/v9/internal/proto"
  11. )
  12. // ErrClosed performs any operation on the closed client will return this error.
  13. var ErrClosed = pool.ErrClosed
  14. // ErrPoolExhausted is returned from a pool connection method
  15. // when the maximum number of database connections in the pool has been reached.
  16. var ErrPoolExhausted = pool.ErrPoolExhausted
  17. // ErrPoolTimeout timed out waiting to get a connection from the connection pool.
  18. var ErrPoolTimeout = pool.ErrPoolTimeout
  19. // ErrCrossSlot is returned when keys are used in the same Redis command and
  20. // the keys are not in the same hash slot. This error is returned by Redis
  21. // Cluster and will be returned by the client when TxPipeline or TxPipelined
  22. // is used on a ClusterClient with keys in different slots.
  23. var ErrCrossSlot = proto.RedisError("CROSSSLOT Keys in request don't hash to the same slot")
  24. // HasErrorPrefix checks if the err is a Redis error and the message contains a prefix.
  25. func HasErrorPrefix(err error, prefix string) bool {
  26. var rErr Error
  27. if !errors.As(err, &rErr) {
  28. return false
  29. }
  30. msg := rErr.Error()
  31. msg = strings.TrimPrefix(msg, "ERR ") // KVRocks adds such prefix
  32. return strings.HasPrefix(msg, prefix)
  33. }
  34. type Error interface {
  35. error
  36. // RedisError is a no-op function but
  37. // serves to distinguish types that are Redis
  38. // errors from ordinary errors: a type is a
  39. // Redis error if it has a RedisError method.
  40. RedisError()
  41. }
  42. var _ Error = proto.RedisError("")
  43. func isContextError(err error) bool {
  44. switch err {
  45. case context.Canceled, context.DeadlineExceeded:
  46. return true
  47. default:
  48. return false
  49. }
  50. }
  51. func shouldRetry(err error, retryTimeout bool) bool {
  52. switch err {
  53. case io.EOF, io.ErrUnexpectedEOF:
  54. return true
  55. case nil, context.Canceled, context.DeadlineExceeded:
  56. return false
  57. case pool.ErrPoolTimeout:
  58. // connection pool timeout, increase retries. #3289
  59. return true
  60. }
  61. if v, ok := err.(timeoutError); ok {
  62. if v.Timeout() {
  63. return retryTimeout
  64. }
  65. return true
  66. }
  67. s := err.Error()
  68. if s == "ERR max number of clients reached" {
  69. return true
  70. }
  71. if strings.HasPrefix(s, "LOADING ") {
  72. return true
  73. }
  74. if strings.HasPrefix(s, "READONLY ") {
  75. return true
  76. }
  77. if strings.HasPrefix(s, "MASTERDOWN ") {
  78. return true
  79. }
  80. if strings.HasPrefix(s, "CLUSTERDOWN ") {
  81. return true
  82. }
  83. if strings.HasPrefix(s, "TRYAGAIN ") {
  84. return true
  85. }
  86. return false
  87. }
  88. func isRedisError(err error) bool {
  89. _, ok := err.(proto.RedisError)
  90. return ok
  91. }
  92. func isBadConn(err error, allowTimeout bool, addr string) bool {
  93. switch err {
  94. case nil:
  95. return false
  96. case context.Canceled, context.DeadlineExceeded:
  97. return true
  98. case pool.ErrConnUnusableTimeout:
  99. return true
  100. }
  101. if isRedisError(err) {
  102. switch {
  103. case isReadOnlyError(err):
  104. // Close connections in read only state in case domain addr is used
  105. // and domain resolves to a different Redis Server. See #790.
  106. return true
  107. case isMovedSameConnAddr(err, addr):
  108. // Close connections when we are asked to move to the same addr
  109. // of the connection. Force a DNS resolution when all connections
  110. // of the pool are recycled
  111. return true
  112. default:
  113. return false
  114. }
  115. }
  116. if allowTimeout {
  117. if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
  118. return false
  119. }
  120. }
  121. return true
  122. }
  123. func isMovedError(err error) (moved bool, ask bool, addr string) {
  124. if !isRedisError(err) {
  125. return
  126. }
  127. s := err.Error()
  128. switch {
  129. case strings.HasPrefix(s, "MOVED "):
  130. moved = true
  131. case strings.HasPrefix(s, "ASK "):
  132. ask = true
  133. default:
  134. return
  135. }
  136. ind := strings.LastIndex(s, " ")
  137. if ind == -1 {
  138. return false, false, ""
  139. }
  140. addr = s[ind+1:]
  141. addr = internal.GetAddr(addr)
  142. return
  143. }
  144. func isLoadingError(err error) bool {
  145. return strings.HasPrefix(err.Error(), "LOADING ")
  146. }
  147. func isReadOnlyError(err error) bool {
  148. return strings.HasPrefix(err.Error(), "READONLY ")
  149. }
  150. func isMovedSameConnAddr(err error, addr string) bool {
  151. redisError := err.Error()
  152. if !strings.HasPrefix(redisError, "MOVED ") {
  153. return false
  154. }
  155. return strings.HasSuffix(redisError, " "+addr)
  156. }
  157. //------------------------------------------------------------------------------
  158. type timeoutError interface {
  159. Timeout() bool
  160. }