1
0
mirror of https://github.com/redis/go-redis.git synced 2025-07-28 06:42:00 +03:00

Merge branch 'master' into ndyakov/token-based-auth

This commit is contained in:
Nedyalko Dyakov
2025-04-29 16:03:57 +03:00
committed by GitHub
18 changed files with 147 additions and 32 deletions

View File

@ -25,7 +25,7 @@ runs:
# Mapping of redis version to redis testing containers # Mapping of redis version to redis testing containers
declare -A redis_version_mapping=( declare -A redis_version_mapping=(
["8.0-RC1"]="8.0-RC1-pre" ["8.0-RC2"]="8.0-RC2-pre"
["7.4.2"]="rs-7.4.0-v2" ["7.4.2"]="rs-7.4.0-v2"
["7.2.7"]="rs-7.2.0-v14" ["7.2.7"]="rs-7.2.0-v14"
) )

View File

@ -18,7 +18,7 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
redis-version: redis-version:
- "8.0-RC1" # 8.0 RC1 - "8.0-RC2" # 8.0 RC2
- "7.4.2" # should use redis stack 7.4 - "7.4.2" # should use redis stack 7.4
go-version: go-version:
- "1.23.x" - "1.23.x"
@ -43,7 +43,7 @@ jobs:
# Mapping of redis version to redis testing containers # Mapping of redis version to redis testing containers
declare -A redis_version_mapping=( declare -A redis_version_mapping=(
["8.0-RC1"]="8.0-RC1-pre" ["8.0-RC2"]="8.0-RC2-pre"
["7.4.2"]="rs-7.4.0-v2" ["7.4.2"]="rs-7.4.0-v2"
) )
if [[ -v redis_version_mapping[$REDIS_VERSION] ]]; then if [[ -v redis_version_mapping[$REDIS_VERSION] ]]; then
@ -72,7 +72,7 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
redis-version: redis-version:
- "8.0-RC1" # 8.0 RC1 - "8.0-RC2" # 8.0 RC2
- "7.4.2" # should use redis stack 7.4 - "7.4.2" # should use redis stack 7.4
- "7.2.7" # should redis stack 7.2 - "7.2.7" # should redis stack 7.2
go-version: go-version:

View File

@ -21,7 +21,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: golangci-lint - name: golangci-lint
uses: golangci/golangci-lint-action@v6.5.2 uses: golangci/golangci-lint-action@v7.0.0
with: with:
verify: false # disable verifying the configuration since golangci is currently introducing breaking changes in the configuration verify: true

View File

@ -1,3 +1,34 @@
version: "2"
run: run:
timeout: 5m timeout: 5m
tests: false tests: false
linters:
settings:
staticcheck:
checks:
- all
# Incorrect or missing package comment.
# https://staticcheck.dev/docs/checks/#ST1000
- -ST1000
# Omit embedded fields from selector expression.
# https://staticcheck.dev/docs/checks/#QF1008
- -QF1008
- -ST1003
exclusions:
generated: lax
presets:
- comments
- common-false-positives
- legacy
- std-error-handling
paths:
- third_party$
- builtin$
- examples$
formatters:
exclusions:
generated: lax
paths:
- third_party$
- builtin$
- examples$

View File

@ -1412,7 +1412,8 @@ func (cmd *MapStringSliceInterfaceCmd) readReply(rd *proto.Reader) (err error) {
cmd.val = make(map[string][]interface{}) cmd.val = make(map[string][]interface{})
if readType == proto.RespMap { switch readType {
case proto.RespMap:
n, err := rd.ReadMapLen() n, err := rd.ReadMapLen()
if err != nil { if err != nil {
return err return err
@ -1435,7 +1436,7 @@ func (cmd *MapStringSliceInterfaceCmd) readReply(rd *proto.Reader) (err error) {
cmd.val[k][j] = value cmd.val[k][j] = value
} }
} }
} else if readType == proto.RespArray { case proto.RespArray:
// RESP2 response // RESP2 response
n, err := rd.ReadArrayLen() n, err := rd.ReadArrayLen()
if err != nil { if err != nil {

View File

@ -155,6 +155,12 @@ func isEmptyValue(v reflect.Value) bool {
return v.Float() == 0 return v.Float() == 0
case reflect.Interface, reflect.Pointer: case reflect.Interface, reflect.Pointer:
return v.IsNil() return v.IsNil()
case reflect.Struct:
if v.Type() == reflect.TypeOf(time.Time{}) {
return v.IsZero()
}
// Only supports the struct time.Time,
// subsequent iterations will follow the func Scan support decoder.
} }
return false return false
} }

View File

@ -2578,6 +2578,63 @@ var _ = Describe("Commands", func() {
"val2", "val2",
"val", "val",
})) }))
type setOmitEmpty struct {
Set1 string `redis:"set1"`
Set2 int `redis:"set2,omitempty"`
Set3 time.Duration `redis:"set3,omitempty"`
Set4 string `redis:"set4,omitempty"`
Set5 time.Time `redis:"set5,omitempty"`
Set6 *numberStruct `redis:"set6,omitempty"`
Set7 numberStruct `redis:"set7,omitempty"`
}
hSet = client.HSet(ctx, "hash3", &setOmitEmpty{
Set1: "val",
})
Expect(hSet.Err()).NotTo(HaveOccurred())
// both set1 and set7 are set
// custom struct is not omitted
Expect(hSet.Val()).To(Equal(int64(2)))
hGetAll := client.HGetAll(ctx, "hash3")
Expect(hGetAll.Err()).NotTo(HaveOccurred())
Expect(hGetAll.Val()).To(Equal(map[string]string{
"set1": "val",
"set7": `{"Number":0}`,
}))
var hash3 setOmitEmpty
Expect(hGetAll.Scan(&hash3)).NotTo(HaveOccurred())
Expect(hash3.Set1).To(Equal("val"))
Expect(hash3.Set2).To(Equal(0))
Expect(hash3.Set3).To(Equal(time.Duration(0)))
Expect(hash3.Set4).To(Equal(""))
Expect(hash3.Set5).To(Equal(time.Time{}))
Expect(hash3.Set6).To(BeNil())
Expect(hash3.Set7).To(Equal(numberStruct{}))
now := time.Now()
hSet = client.HSet(ctx, "hash4", setOmitEmpty{
Set1: "val",
Set5: now,
Set6: &numberStruct{
Number: 5,
},
Set7: numberStruct{
Number: 3,
},
})
Expect(hSet.Err()).NotTo(HaveOccurred())
Expect(hSet.Val()).To(Equal(int64(4)))
hGetAll = client.HGetAll(ctx, "hash4")
Expect(hGetAll.Err()).NotTo(HaveOccurred())
Expect(hGetAll.Val()).To(Equal(map[string]string{
"set1": "val",
"set5": now.Format(time.RFC3339Nano),
"set6": `{"Number":5}`,
"set7": `{"Number":3}`,
}))
}) })
It("should HSetNX", func() { It("should HSetNX", func() {
@ -7619,12 +7676,16 @@ type numberStruct struct {
Number int Number int
} }
func (s *numberStruct) MarshalBinary() ([]byte, error) { func (n numberStruct) MarshalBinary() ([]byte, error) {
return json.Marshal(s) return json.Marshal(n)
} }
func (s *numberStruct) UnmarshalBinary(b []byte) error { func (n *numberStruct) UnmarshalBinary(b []byte) error {
return json.Unmarshal(b, s) return json.Unmarshal(b, n)
}
func (n *numberStruct) ScanRedis(str string) error {
return json.Unmarshal([]byte(str), n)
} }
func deref(viface interface{}) interface{} { func deref(viface interface{}) interface{} {

View File

@ -19,6 +19,6 @@ require (
) )
retract ( retract (
v9.5.3 // This version was accidentally released.
v9.7.2 // This version was accidentally released. v9.7.2 // This version was accidentally released.
v9.5.3 // This version was accidentally released.
) )

View File

@ -16,6 +16,6 @@ require (
) )
retract ( retract (
v9.5.3 // This version was accidentally released.
v9.7.2 // This version was accidentally released. v9.7.2 // This version was accidentally released.
v9.5.3 // This version was accidentally released.
) )

View File

@ -24,6 +24,6 @@ require (
) )
retract ( retract (
v9.5.3 // This version was accidentally released.
v9.7.2 // This version was accidentally released. v9.7.2 // This version was accidentally released.
v9.5.3 // This version was accidentally released.
) )

View File

@ -23,6 +23,6 @@ require (
) )
retract ( retract (
v9.5.3 // This version was accidentally released.
v9.7.2 // This version was accidentally released. v9.7.2 // This version was accidentally released.
v9.5.3 // This version was accidentally released.
) )

View File

@ -265,9 +265,10 @@ func (opt *Options) init() {
opt.ConnMaxIdleTime = 30 * time.Minute opt.ConnMaxIdleTime = 30 * time.Minute
} }
if opt.MaxRetries == -1 { switch opt.MaxRetries {
case -1:
opt.MaxRetries = 0 opt.MaxRetries = 0
} else if opt.MaxRetries == 0 { case 0:
opt.MaxRetries = 3 opt.MaxRetries = 3
} }
switch opt.MinRetryBackoff { switch opt.MinRetryBackoff {

View File

@ -113,9 +113,10 @@ type ClusterOptions struct {
} }
func (opt *ClusterOptions) init() { func (opt *ClusterOptions) init() {
if opt.MaxRedirects == -1 { switch opt.MaxRedirects {
case -1:
opt.MaxRedirects = 0 opt.MaxRedirects = 0
} else if opt.MaxRedirects == 0 { case 0:
opt.MaxRedirects = 3 opt.MaxRedirects = 3
} }

View File

@ -45,6 +45,9 @@ func (c *PubSub) init() {
} }
func (c *PubSub) String() string { func (c *PubSub) String() string {
c.mu.Lock()
defer c.mu.Unlock()
channels := mapKeys(c.channels) channels := mapKeys(c.channels)
channels = append(channels, mapKeys(c.patterns)...) channels = append(channels, mapKeys(c.patterns)...)
channels = append(channels, mapKeys(c.schannels)...) channels = append(channels, mapKeys(c.schannels)...)

View File

@ -128,9 +128,10 @@ func (opt *RingOptions) init() {
opt.NewConsistentHash = newRendezvous opt.NewConsistentHash = newRendezvous
} }
if opt.MaxRetries == -1 { switch opt.MaxRetries {
case -1:
opt.MaxRetries = 0 opt.MaxRetries = 0
} else if opt.MaxRetries == 0 { case 0:
opt.MaxRetries = 3 opt.MaxRetries = 3
} }
switch opt.MinRetryBackoff { switch opt.MinRetryBackoff {

View File

@ -41,7 +41,7 @@ var _ = Describe("Sentinel resolution", func() {
client := redis.NewFailoverClient(&redis.FailoverOptions{ client := redis.NewFailoverClient(&redis.FailoverOptions{
MasterName: sentinelName, MasterName: sentinelName,
SentinelAddrs: sentinelAddrs, SentinelAddrs: sentinelAddrs,
MaxRetries: -1, MaxRetries: -1,
}) })
err := client.Ping(shortCtx).Err() err := client.Ping(shortCtx).Err()

View File

@ -269,11 +269,21 @@ var _ = Describe("RedisTimeseries commands", Label("timeseries"), func() {
if client.Options().Protocol == 2 { if client.Options().Protocol == 2 {
Expect(resultInfo["labels"].([]interface{})[0]).To(BeEquivalentTo([]interface{}{"Time", "Series"})) Expect(resultInfo["labels"].([]interface{})[0]).To(BeEquivalentTo([]interface{}{"Time", "Series"}))
Expect(resultInfo["retentionTime"]).To(BeEquivalentTo(10)) Expect(resultInfo["retentionTime"]).To(BeEquivalentTo(10))
Expect(resultInfo["duplicatePolicy"]).To(BeEquivalentTo(redis.Nil)) if RedisVersion >= 8 {
Expect(resultInfo["duplicatePolicy"]).To(BeEquivalentTo("block"))
} else {
// Older versions of Redis had a bug where the duplicate policy was not set correctly
Expect(resultInfo["duplicatePolicy"]).To(BeEquivalentTo(redis.Nil))
}
} else { } else {
Expect(resultInfo["labels"].(map[interface{}]interface{})["Time"]).To(BeEquivalentTo("Series")) Expect(resultInfo["labels"].(map[interface{}]interface{})["Time"]).To(BeEquivalentTo("Series"))
Expect(resultInfo["retentionTime"]).To(BeEquivalentTo(10)) Expect(resultInfo["retentionTime"]).To(BeEquivalentTo(10))
Expect(resultInfo["duplicatePolicy"]).To(BeEquivalentTo(redis.Nil)) if RedisVersion >= 8 {
Expect(resultInfo["duplicatePolicy"]).To(BeEquivalentTo("block"))
} else {
// Older versions of Redis had a bug where the duplicate policy was not set correctly
Expect(resultInfo["duplicatePolicy"]).To(BeEquivalentTo(redis.Nil))
}
} }
opt = &redis.TSAlterOptions{DuplicatePolicy: "min"} opt = &redis.TSAlterOptions{DuplicatePolicy: "min"}
resultAlter, err = client.TSAlter(ctx, "1", opt).Result() resultAlter, err = client.TSAlter(ctx, "1", opt).Result()

View File

@ -259,13 +259,13 @@ var (
// NewUniversalClient returns a new multi client. The type of the returned client depends // NewUniversalClient returns a new multi client. The type of the returned client depends
// on the following conditions: // on the following conditions:
// //
// 1. If the MasterName option is specified with RouteByLatency, RouteRandomly or IsClusterMode, // 1. If the MasterName option is specified with RouteByLatency, RouteRandomly or IsClusterMode,
// a FailoverClusterClient is returned. // a FailoverClusterClient is returned.
// 2. If the MasterName option is specified without RouteByLatency, RouteRandomly or IsClusterMode, // 2. If the MasterName option is specified without RouteByLatency, RouteRandomly or IsClusterMode,
// a sentinel-backed FailoverClient is returned. // a sentinel-backed FailoverClient is returned.
// 3. If the number of Addrs is two or more, or IsClusterMode option is specified, // 3. If the number of Addrs is two or more, or IsClusterMode option is specified,
// a ClusterClient is returned. // a ClusterClient is returned.
// 4. Otherwise, a single-node Client is returned. // 4. Otherwise, a single-node Client is returned.
func NewUniversalClient(opts *UniversalOptions) UniversalClient { func NewUniversalClient(opts *UniversalOptions) UniversalClient {
switch { switch {
case opts.MasterName != "" && (opts.RouteByLatency || opts.RouteRandomly || opts.IsClusterMode): case opts.MasterName != "" && (opts.RouteByLatency || opts.RouteRandomly || opts.IsClusterMode):