diff --git a/cluster_test.go b/cluster_test.go index 076f4b67..2bf5d916 100644 --- a/cluster_test.go +++ b/cluster_test.go @@ -574,8 +574,8 @@ var _ = Describe("ClusterClient", func() { Describe("ClusterClient failover", func() { BeforeEach(func() { opt = redisClusterOptions() - opt.MinRetryBackoff = 100 * time.Millisecond - opt.MaxRetryBackoff = 3 * time.Second + opt.MinRetryBackoff = 250 * time.Millisecond + opt.MaxRetryBackoff = time.Second client = cluster.clusterClient(opt) _ = client.ForEachSlave(func(slave *redis.Client) error { diff --git a/command.go b/command.go index 480a5ce1..7d45c3fa 100644 --- a/command.go +++ b/command.go @@ -10,6 +10,7 @@ import ( "github.com/go-redis/redis/internal" "github.com/go-redis/redis/internal/pool" "github.com/go-redis/redis/internal/proto" + "github.com/go-redis/redis/internal/util" ) type Cmder interface { @@ -436,7 +437,7 @@ func NewStringCmd(args ...interface{}) *StringCmd { } func (cmd *StringCmd) Val() string { - return internal.BytesToString(cmd.val) + return util.BytesToString(cmd.val) } func (cmd *StringCmd) Result() (string, error) { diff --git a/commands.go b/commands.go index 92a8b7a5..45bb0aed 100644 --- a/commands.go +++ b/commands.go @@ -1,6 +1,7 @@ package redis import ( + "errors" "io" "time" @@ -1802,7 +1803,7 @@ func (c *cmdable) shutdown(modifier string) *StatusCmd { } } else { // Server did not quit. String reply contains the reason. - cmd.err = internal.RedisError(cmd.val) + cmd.err = errors.New(cmd.val) cmd.val = "" } return cmd diff --git a/commands_test.go b/commands_test.go index 524c3ac7..b173e0d4 100644 --- a/commands_test.go +++ b/commands_test.go @@ -10,7 +10,7 @@ import ( . "github.com/onsi/gomega" "github.com/go-redis/redis" - "github.com/go-redis/redis/internal" + "github.com/go-redis/redis/internal/proto" ) var _ = Describe("Commands", func() { @@ -3000,7 +3000,7 @@ var _ = Describe("Commands", func() { nil, ).Result() Expect(err).NotTo(HaveOccurred()) - Expect(vals).To(Equal([]interface{}{int64(12), internal.RedisError("error"), "abc"})) + Expect(vals).To(Equal([]interface{}{int64(12), proto.RedisError("error"), "abc"})) }) }) diff --git a/internal/error.go b/internal/error.go index c190f469..7b419577 100644 --- a/internal/error.go +++ b/internal/error.go @@ -4,14 +4,10 @@ import ( "io" "net" "strings" + + "github.com/go-redis/redis/internal/proto" ) -const Nil = RedisError("redis: nil") - -type RedisError string - -func (e RedisError) Error() string { return string(e) } - func IsRetryableError(err error, retryNetError bool) bool { if IsNetworkError(err) { return retryNetError @@ -30,7 +26,7 @@ func IsRetryableError(err error, retryNetError bool) bool { } func IsRedisError(err error) bool { - _, ok := err.(RedisError) + _, ok := err.(proto.RedisError) return ok } diff --git a/internal/proto/reader.go b/internal/proto/reader.go index e5ae8a03..d5d69535 100644 --- a/internal/proto/reader.go +++ b/internal/proto/reader.go @@ -6,7 +6,7 @@ import ( "io" "strconv" - "github.com/go-redis/redis/internal" + "github.com/go-redis/redis/internal/util" ) const bytesAllocLimit = 1024 * 1024 // 1mb @@ -19,6 +19,16 @@ const ( ArrayReply = '*' ) +//------------------------------------------------------------------------------ + +const Nil = RedisError("redis: nil") + +type RedisError string + +func (e RedisError) Error() string { return string(e) } + +//------------------------------------------------------------------------------ + type MultiBulkParse func(*Reader, int64) (interface{}, error) type Reader struct { @@ -66,7 +76,7 @@ func (r *Reader) ReadLine() ([]byte, error) { return nil, fmt.Errorf("redis: reply is empty") } if isNilReply(line) { - return nil, internal.Nil + return nil, Nil } return line, nil } @@ -83,7 +93,7 @@ func (r *Reader) ReadReply(m MultiBulkParse) (interface{}, error) { case StatusReply: return parseStatusValue(line), nil case IntReply: - return parseInt(line[1:], 10, 64) + return util.ParseInt(line[1:], 10, 64) case StringReply: return r.readTmpBytesValue(line) case ArrayReply: @@ -105,7 +115,7 @@ func (r *Reader) ReadIntReply() (int64, error) { case ErrorReply: return 0, ParseErrorReply(line) case IntReply: - return parseInt(line[1:], 10, 64) + return util.ParseInt(line[1:], 10, 64) default: return 0, fmt.Errorf("redis: can't parse int reply: %.100q", line) } @@ -151,7 +161,7 @@ func (r *Reader) ReadFloatReply() (float64, error) { if err != nil { return 0, err } - return parseFloat(b, 64) + return util.ParseFloat(b, 64) } func (r *Reader) ReadArrayReply(m MultiBulkParse) (interface{}, error) { @@ -221,7 +231,7 @@ func (r *Reader) ReadScanReply() ([]string, uint64, error) { func (r *Reader) readTmpBytesValue(line []byte) ([]byte, error) { if isNilReply(line) { - return nil, internal.Nil + return nil, Nil } replyLen, err := strconv.Atoi(string(line[1:])) @@ -241,7 +251,7 @@ func (r *Reader) ReadInt() (int64, error) { if err != nil { return 0, err } - return parseInt(b, 10, 64) + return util.ParseInt(b, 10, 64) } func (r *Reader) ReadUint() (uint64, error) { @@ -249,7 +259,7 @@ func (r *Reader) ReadUint() (uint64, error) { if err != nil { return 0, err } - return parseUint(b, 10, 64) + return util.ParseUint(b, 10, 64) } // -------------------------------------------------------------------- @@ -303,7 +313,7 @@ func isNilReply(b []byte) bool { } func ParseErrorReply(line []byte) error { - return internal.RedisError(string(line[1:])) + return RedisError(string(line[1:])) } func parseStatusValue(line []byte) []byte { @@ -312,23 +322,7 @@ func parseStatusValue(line []byte) []byte { func parseArrayLen(line []byte) (int64, error) { if isNilReply(line) { - return 0, internal.Nil + return 0, Nil } - return parseInt(line[1:], 10, 64) -} - -func atoi(b []byte) (int, error) { - return strconv.Atoi(internal.BytesToString(b)) -} - -func parseInt(b []byte, base int, bitSize int) (int64, error) { - return strconv.ParseInt(internal.BytesToString(b), base, bitSize) -} - -func parseUint(b []byte, base int, bitSize int) (uint64, error) { - return strconv.ParseUint(internal.BytesToString(b), base, bitSize) -} - -func parseFloat(b []byte, bitSize int) (float64, error) { - return strconv.ParseFloat(internal.BytesToString(b), bitSize) + return util.ParseInt(line[1:], 10, 64) } diff --git a/internal/proto/scan.go b/internal/proto/scan.go index 0329ffd9..3bdb33f9 100644 --- a/internal/proto/scan.go +++ b/internal/proto/scan.go @@ -5,7 +5,7 @@ import ( "fmt" "reflect" - "github.com/go-redis/redis/internal" + "github.com/go-redis/redis/internal/util" ) func Scan(b []byte, v interface{}) error { @@ -13,80 +13,80 @@ func Scan(b []byte, v interface{}) error { case nil: return fmt.Errorf("redis: Scan(nil)") case *string: - *v = internal.BytesToString(b) + *v = util.BytesToString(b) return nil case *[]byte: *v = b return nil case *int: var err error - *v, err = atoi(b) + *v, err = util.Atoi(b) return err case *int8: - n, err := parseInt(b, 10, 8) + n, err := util.ParseInt(b, 10, 8) if err != nil { return err } *v = int8(n) return nil case *int16: - n, err := parseInt(b, 10, 16) + n, err := util.ParseInt(b, 10, 16) if err != nil { return err } *v = int16(n) return nil case *int32: - n, err := parseInt(b, 10, 32) + n, err := util.ParseInt(b, 10, 32) if err != nil { return err } *v = int32(n) return nil case *int64: - n, err := parseInt(b, 10, 64) + n, err := util.ParseInt(b, 10, 64) if err != nil { return err } *v = n return nil case *uint: - n, err := parseUint(b, 10, 64) + n, err := util.ParseUint(b, 10, 64) if err != nil { return err } *v = uint(n) return nil case *uint8: - n, err := parseUint(b, 10, 8) + n, err := util.ParseUint(b, 10, 8) if err != nil { return err } *v = uint8(n) return nil case *uint16: - n, err := parseUint(b, 10, 16) + n, err := util.ParseUint(b, 10, 16) if err != nil { return err } *v = uint16(n) return nil case *uint32: - n, err := parseUint(b, 10, 32) + n, err := util.ParseUint(b, 10, 32) if err != nil { return err } *v = uint32(n) return nil case *uint64: - n, err := parseUint(b, 10, 64) + n, err := util.ParseUint(b, 10, 64) if err != nil { return err } *v = n return nil case *float32: - n, err := parseFloat(b, 32) + n, err := util.ParseFloat(b, 32) if err != nil { return err } @@ -94,7 +94,7 @@ func Scan(b []byte, v interface{}) error { return err case *float64: var err error - *v, err = parseFloat(b, 64) + *v, err = util.ParseFloat(b, 64) return err case *bool: *v = len(b) == 1 && b[0] == '1' @@ -120,7 +120,7 @@ func ScanSlice(data []string, slice interface{}) error { return fmt.Errorf("redis: ScanSlice(non-slice %T)", slice) } - next := internal.MakeSliceNextElemFunc(v) + next := makeSliceNextElemFunc(v) for i, s := range data { elem := next() if err := Scan([]byte(s), elem.Addr().Interface()); err != nil { @@ -131,3 +131,36 @@ func ScanSlice(data []string, slice interface{}) error { return nil } + +func makeSliceNextElemFunc(v reflect.Value) func() reflect.Value { + elemType := v.Type().Elem() + + if elemType.Kind() == reflect.Ptr { + elemType = elemType.Elem() + return func() reflect.Value { + if v.Len() < v.Cap() { + v.Set(v.Slice(0, v.Len()+1)) + elem := v.Index(v.Len() - 1) + if elem.IsNil() { + elem.Set(reflect.New(elemType)) + } + return elem.Elem() + } + + elem := reflect.New(elemType) + v.Set(reflect.Append(v, elem)) + return elem.Elem() + } + } + + zero := reflect.Zero(elemType) + return func() reflect.Value { + if v.Len() < v.Cap() { + v.Set(v.Slice(0, v.Len()+1)) + return v.Index(v.Len() - 1) + } + + v.Set(reflect.Append(v, zero)) + return v.Index(v.Len() - 1) + } +} diff --git a/internal/util.go b/internal/util.go index 1ba9805f..ffd2353e 100644 --- a/internal/util.go +++ b/internal/util.go @@ -1,6 +1,6 @@ package internal -import "reflect" +import "github.com/go-redis/redis/internal/util" func ToLower(s string) string { if isLower(s) { @@ -15,7 +15,7 @@ func ToLower(s string) string { } b[i] = c } - return BytesToString(b) + return util.BytesToString(b) } func isLower(s string) bool { @@ -27,36 +27,3 @@ func isLower(s string) bool { } return true } - -func MakeSliceNextElemFunc(v reflect.Value) func() reflect.Value { - elemType := v.Type().Elem() - - if elemType.Kind() == reflect.Ptr { - elemType = elemType.Elem() - return func() reflect.Value { - if v.Len() < v.Cap() { - v.Set(v.Slice(0, v.Len()+1)) - elem := v.Index(v.Len() - 1) - if elem.IsNil() { - elem.Set(reflect.New(elemType)) - } - return elem.Elem() - } - - elem := reflect.New(elemType) - v.Set(reflect.Append(v, elem)) - return elem.Elem() - } - } - - zero := reflect.Zero(elemType) - return func() reflect.Value { - if v.Len() < v.Cap() { - v.Set(v.Slice(0, v.Len()+1)) - return v.Index(v.Len() - 1) - } - - v.Set(reflect.Append(v, zero)) - return v.Index(v.Len() - 1) - } -} diff --git a/internal/safe.go b/internal/util/safe.go similarity index 82% rename from internal/safe.go rename to internal/util/safe.go index dc5f4cc8..cd891833 100644 --- a/internal/safe.go +++ b/internal/util/safe.go @@ -1,6 +1,6 @@ // +build appengine -package internal +package util func BytesToString(b []byte) string { return string(b) diff --git a/internal/util/strconv.go b/internal/util/strconv.go new file mode 100644 index 00000000..db503380 --- /dev/null +++ b/internal/util/strconv.go @@ -0,0 +1,19 @@ +package util + +import "strconv" + +func Atoi(b []byte) (int, error) { + return strconv.Atoi(BytesToString(b)) +} + +func ParseInt(b []byte, base int, bitSize int) (int64, error) { + return strconv.ParseInt(BytesToString(b), base, bitSize) +} + +func ParseUint(b []byte, base int, bitSize int) (uint64, error) { + return strconv.ParseUint(BytesToString(b), base, bitSize) +} + +func ParseFloat(b []byte, bitSize int) (float64, error) { + return strconv.ParseFloat(BytesToString(b), bitSize) +} diff --git a/internal/unsafe.go b/internal/util/unsafe.go similarity index 91% rename from internal/unsafe.go rename to internal/util/unsafe.go index 3ae48c14..93a89c55 100644 --- a/internal/unsafe.go +++ b/internal/util/unsafe.go @@ -1,6 +1,6 @@ // +build !appengine -package internal +package util import ( "unsafe" diff --git a/parser.go b/parser.go index e77efeeb..f0dc67f0 100644 --- a/parser.go +++ b/parser.go @@ -14,17 +14,23 @@ func sliceParser(rd *proto.Reader, n int64) (interface{}, error) { vals := make([]interface{}, 0, n) for i := int64(0); i < n; i++ { v, err := rd.ReadReply(sliceParser) - if err == Nil { - vals = append(vals, nil) - } else if err != nil { - vals = append(vals, err) - } else { - switch vv := v.(type) { - case []byte: - vals = append(vals, string(vv)) - default: - vals = append(vals, v) + if err != nil { + if err == Nil { + vals = append(vals, nil) + continue } + if err, ok := err.(proto.RedisError); ok { + vals = append(vals, err) + continue + } + return nil, err + } + + switch v := v.(type) { + case []byte: + vals = append(vals, string(v)) + default: + vals = append(vals, v) } } return vals, nil diff --git a/redis.go b/redis.go index cf402986..20f1ed1c 100644 --- a/redis.go +++ b/redis.go @@ -12,7 +12,7 @@ import ( ) // Nil reply redis returned when key does not exist. -const Nil = internal.Nil +const Nil = proto.Nil func init() { SetLogger(log.New(os.Stderr, "redis: ", log.LstdFlags|log.Lshortfile)) diff --git a/tx.go b/tx.go index 26c29bef..6a753b6a 100644 --- a/tx.go +++ b/tx.go @@ -1,12 +1,12 @@ package redis import ( - "github.com/go-redis/redis/internal" "github.com/go-redis/redis/internal/pool" + "github.com/go-redis/redis/internal/proto" ) // TxFailedErr transaction redis failed. -const TxFailedErr = internal.RedisError("redis: transaction failed") +const TxFailedErr = proto.RedisError("redis: transaction failed") // Tx implements Redis transactions as described in // http://redis.io/topics/transactions. It's NOT safe for concurrent use