1
0
mirror of https://github.com/redis/go-redis.git synced 2025-12-18 23:34:11 +03:00

chore(go): update go version to 1.21 (#3640)

* chore(go): update go version to 1.21

* chore(aggregators): make aggregators work with 1.21

* fix doctests

* address copilot comments

* use atomic bool for logic and/or aggregators

* Update .github/workflows/build.yml

Co-authored-by: ccoVeille <3875889+ccoVeille@users.noreply.github.com>

* use stable/oldstable, 1.23 and 1.21

* fix versions in README

* add oldstable in wordlist

---------

Co-authored-by: ccoVeille <3875889+ccoVeille@users.noreply.github.com>
This commit is contained in:
Nedyalko Dyakov
2025-12-05 12:27:55 +02:00
committed by GitHub
parent 16b55f9519
commit 674c7b8695
23 changed files with 329 additions and 45 deletions

View File

@@ -76,3 +76,4 @@ oauth
entraid
MiB
KiB
oldstable

View File

@@ -22,8 +22,10 @@ jobs:
- "8.2.x" # Redis CE 8.2
- "8.0.x" # Redis CE 8.0
go-version:
- "1.21.x"
- "1.23.x"
- "1.24.x"
- oldstable
- stable
steps:
- name: Set up ${{ matrix.go-version }}
@@ -78,8 +80,10 @@ jobs:
- "8.2.x" # Redis CE 8.2
- "8.0.x" # Redis CE 8.0
go-version:
- "1.21.x"
- "1.23.x"
- "1.24.x"
- oldstable
- stable
steps:
- name: Checkout code

View File

@@ -21,13 +21,12 @@ In `go-redis` we are aiming to support the last three releases of Redis. Current
- [Redis 8.2](https://raw.githubusercontent.com/redis/redis/8.2/00-RELEASENOTES) - using Redis CE 8.2
- [Redis 8.4](https://raw.githubusercontent.com/redis/redis/8.4/00-RELEASENOTES) - using Redis CE 8.4
Although the `go.mod` states it requires at minimum `go 1.18`, our CI is configured to run the tests against all three
versions of Redis and latest two versions of Go ([1.23](https://go.dev/doc/devel/release#go1.23.0),
[1.24](https://go.dev/doc/devel/release#go1.24.0)). We observe that some modules related test may not pass with
Although the `go.mod` states it requires at minimum `go 1.21`, our CI is configured to run the tests against all three
versions of Redis and multiple versions of Go ([1.21](https://go.dev/doc/devel/release#go1.21.0),
[1.23](https://go.dev/doc/devel/release#go1.23.0), oldstable, and stable). We observe that some modules related test may not pass with
Redis Stack 7.2 and some commands are changed with Redis CE 8.0.
Although it is not officially supported, `go-redis/v9` should be able to work with any Redis 7.0+.
Please do refer to the documentation and the tests if you experience any issues. We do plan to update the go version
in the `go.mod` to `go 1.24` in one of the next releases.
Please do refer to the documentation and the tests if you experience any issues.
## How do I Redis?

View File

@@ -5,9 +5,7 @@ package example_commands_test
import (
"context"
"fmt"
"maps"
"math"
"slices"
"sort"
"github.com/redis/go-redis/v9"
@@ -15,6 +13,16 @@ import (
// HIDE_END
// mapKeys returns a slice of all keys from the map (Go 1.21 compatible)
// TODO: Once minimum Go version is upgraded to 1.23+, replace with slices.Collect(maps.Keys(m))
func mapKeys[K comparable, V any](m map[K]V) []K {
keys := make([]K, 0, len(m))
for k := range m {
keys = append(keys, k)
}
return keys
}
func ExampleClient_timeseries_create() {
ctx := context.Background()
@@ -417,7 +425,7 @@ func ExampleClient_timeseries_query_multi() {
panic(err)
}
res28Keys := slices.Collect(maps.Keys(res28))
res28Keys := mapKeys(res28)
sort.Strings(res28Keys)
for _, k := range res28Keys {
@@ -457,7 +465,7 @@ func ExampleClient_timeseries_query_multi() {
panic(err)
}
res29Keys := slices.Collect(maps.Keys(res29))
res29Keys := mapKeys(res29)
sort.Strings(res29Keys)
for _, k := range res29Keys {
@@ -505,7 +513,7 @@ func ExampleClient_timeseries_query_multi() {
panic(err)
}
res30Keys := slices.Collect(maps.Keys(res30))
res30Keys := mapKeys(res30)
sort.Strings(res30Keys)
for _, k := range res30Keys {
@@ -550,7 +558,7 @@ func ExampleClient_timeseries_query_multi() {
panic(err)
}
res31Keys := slices.Collect(maps.Keys(res31))
res31Keys := mapKeys(res31)
sort.Strings(res31Keys)
for _, k := range res31Keys {
@@ -857,7 +865,7 @@ func ExampleClient_timeseries_aggmulti() {
panic(err)
}
res44Keys := slices.Collect(maps.Keys(res44))
res44Keys := mapKeys(res44)
sort.Strings(res44Keys)
for _, k := range res44Keys {
@@ -905,7 +913,7 @@ func ExampleClient_timeseries_aggmulti() {
panic(err)
}
res45Keys := slices.Collect(maps.Keys(res45))
res45Keys := mapKeys(res45)
sort.Strings(res45Keys)
for _, k := range res45Keys {

View File

@@ -1,6 +1,6 @@
module github.com/redis/go-redis/example/cluster-mget
go 1.18
go 1.21
replace github.com/redis/go-redis/v9 => ../..

View File

@@ -1,6 +1,6 @@
module github.com/redis/go-redis/example/del-keys-without-ttl
go 1.18
go 1.21
replace github.com/redis/go-redis/v9 => ../..

View File

@@ -1,6 +1,6 @@
module github.com/redis/go-redis/example/digest-optimistic-locking
go 1.18
go 1.21
replace github.com/redis/go-redis/v9 => ../..

View File

@@ -1,6 +1,6 @@
module github.com/redis/go-redis/example/hll
go 1.18
go 1.21
replace github.com/redis/go-redis/v9 => ../..

View File

@@ -1,6 +1,6 @@
module github.com/redis/go-redis/example/scan-struct
go 1.18
go 1.21
replace github.com/redis/go-redis/v9 => ../..

View File

@@ -1,6 +1,6 @@
module github.com/redis/go-redis/example/lua-scripting
go 1.18
go 1.21
replace github.com/redis/go-redis/v9 => ../..

View File

@@ -1,6 +1,6 @@
module github.com/redis/go-redis/example/pubsub
go 1.18
go 1.21
replace github.com/redis/go-redis/v9 => ../..

View File

@@ -1,6 +1,6 @@
module github.com/redis/go-redis/example/redis-bloom
go 1.18
go 1.21
replace github.com/redis/go-redis/v9 => ../..

View File

@@ -1,6 +1,6 @@
module github.com/redis/go-redis/example/scan-struct
go 1.18
go 1.21
replace github.com/redis/go-redis/v9 => ../..

View File

@@ -1,6 +1,6 @@
module github.com/redis/go-redis/extra/rediscensus/v9
go 1.19
go 1.21
replace github.com/redis/go-redis/v9 => ../..

View File

@@ -1,6 +1,6 @@
module github.com/redis/go-redis/extra/rediscmd/v9
go 1.19
go 1.21
replace github.com/redis/go-redis/v9 => ../..

View File

@@ -1,6 +1,6 @@
module github.com/redis/go-redis/extra/redisotel/v9
go 1.19
go 1.21
replace github.com/redis/go-redis/v9 => ../..

View File

@@ -1,6 +1,6 @@
module github.com/redis/go-redis/extra/redisprometheus/v9
go 1.19
go 1.21
replace github.com/redis/go-redis/v9 => ../..

2
go.mod
View File

@@ -1,6 +1,6 @@
module github.com/redis/go-redis/v9
go 1.18
go 1.21
require (
github.com/bsm/ginkgo/v2 v2.12.0

3
go.sum
View File

@@ -5,9 +5,12 @@ github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=

View File

@@ -1,6 +1,6 @@
module github.com/redis/go-redis/internal/customvet
go 1.17
go 1.21
require golang.org/x/tools v0.5.0

View File

@@ -65,7 +65,7 @@ func NewResponseAggregator(policy ResponsePolicy, cmdName string) ResponseAggreg
}
case RespAggLogicalAnd:
andAgg := &AggLogicalAndAggregator{}
andAgg.res.Add(1)
andAgg.res.Store(true)
return andAgg
case RespAggLogicalOr:
@@ -466,7 +466,7 @@ func (a *AggMaxAggregator) Result() (interface{}, error) {
// AggLogicalAndAggregator performs logical AND on boolean values.
type AggLogicalAndAggregator struct {
err atomic.Value
res atomic.Int64
res atomic.Bool
hasResult atomic.Bool
}
@@ -482,10 +482,9 @@ func (a *AggLogicalAndAggregator) Add(result interface{}, err error) error {
return e
}
if val {
a.res.And(1)
} else {
a.res.And(0)
// Atomic AND operation: if val is false, result is always false
if !val {
a.res.Store(false)
}
a.hasResult.Store(true)
@@ -544,13 +543,13 @@ func (a *AggLogicalAndAggregator) Result() (interface{}, error) {
if !a.hasResult.Load() {
return nil, ErrAndAggregation
}
return a.res.Load() != 0, nil
return a.res.Load(), nil
}
// AggLogicalOrAggregator performs logical OR on boolean values.
type AggLogicalOrAggregator struct {
err atomic.Value
res atomic.Int64
res atomic.Bool
hasResult atomic.Bool
}
@@ -566,10 +565,9 @@ func (a *AggLogicalOrAggregator) Add(result interface{}, err error) error {
return e
}
// Atomic OR operation: if val is true, result is always true
if val {
a.res.Or(1)
} else {
a.res.Or(0)
a.res.Store(true)
}
a.hasResult.Store(true)
@@ -628,7 +626,7 @@ func (a *AggLogicalOrAggregator) Result() (interface{}, error) {
if !a.hasResult.Load() {
return nil, ErrOrAggregation
}
return a.res.Load() != 0, nil
return a.res.Load(), nil
}
func toInt64(val interface{}) (int64, error) {

View File

@@ -0,0 +1,271 @@
package routing
import (
"errors"
"testing"
)
func TestAggLogicalAndAggregator(t *testing.T) {
t.Run("all true values", func(t *testing.T) {
agg := NewResponseAggregator(RespAggLogicalAnd, "")
err := agg.Add(true, nil)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
err = agg.Add(int64(1), nil)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
err = agg.Add(1, nil)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
result, err := agg.Result()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if result != true {
t.Errorf("expected true, got %v", result)
}
})
t.Run("one false value", func(t *testing.T) {
agg := NewResponseAggregator(RespAggLogicalAnd, "")
err := agg.Add(true, nil)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
err = agg.Add(false, nil)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
err = agg.Add(true, nil)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
result, err := agg.Result()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if result != false {
t.Errorf("expected false, got %v", result)
}
})
t.Run("no results", func(t *testing.T) {
agg := NewResponseAggregator(RespAggLogicalAnd, "")
_, err := agg.Result()
if err != ErrAndAggregation {
t.Errorf("expected ErrAndAggregation, got %v", err)
}
})
t.Run("with error", func(t *testing.T) {
agg := NewResponseAggregator(RespAggLogicalAnd, "")
testErr := errors.New("test error")
err := agg.Add(nil, testErr)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
_, err = agg.Result()
if err != testErr {
t.Errorf("expected test error, got %v", err)
}
})
}
func TestAggLogicalOrAggregator(t *testing.T) {
t.Run("all false values", func(t *testing.T) {
agg := NewResponseAggregator(RespAggLogicalOr, "")
err := agg.Add(false, nil)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
err = agg.Add(int64(0), nil)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
err = agg.Add(0, nil)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
result, err := agg.Result()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if result != false {
t.Errorf("expected false, got %v", result)
}
})
t.Run("one true value", func(t *testing.T) {
agg := NewResponseAggregator(RespAggLogicalOr, "")
err := agg.Add(false, nil)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
err = agg.Add(true, nil)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
err = agg.Add(false, nil)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
result, err := agg.Result()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if result != true {
t.Errorf("expected true, got %v", result)
}
})
t.Run("no results", func(t *testing.T) {
agg := NewResponseAggregator(RespAggLogicalOr, "")
_, err := agg.Result()
if err != ErrOrAggregation {
t.Errorf("expected ErrOrAggregation, got %v", err)
}
})
t.Run("with error", func(t *testing.T) {
agg := NewResponseAggregator(RespAggLogicalOr, "")
testErr := errors.New("test error")
err := agg.Add(nil, testErr)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
_, err = agg.Result()
if err != testErr {
t.Errorf("expected test error, got %v", err)
}
})
}
func TestAggLogicalAndBatchAdd(t *testing.T) {
t.Run("batch add all true", func(t *testing.T) {
agg := NewResponseAggregator(RespAggLogicalAnd, "")
results := map[string]AggregatorResErr{
"key1": {Result: true, Err: nil},
"key2": {Result: int64(1), Err: nil},
"key3": {Result: 1, Err: nil},
}
err := agg.BatchAdd(results)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
result, err := agg.Result()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if result != true {
t.Errorf("expected true, got %v", result)
}
})
t.Run("batch add with false", func(t *testing.T) {
agg := NewResponseAggregator(RespAggLogicalAnd, "")
results := map[string]AggregatorResErr{
"key1": {Result: true, Err: nil},
"key2": {Result: false, Err: nil},
"key3": {Result: true, Err: nil},
}
err := agg.BatchAdd(results)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
result, err := agg.Result()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if result != false {
t.Errorf("expected false, got %v", result)
}
})
}
func TestAggLogicalOrBatchAdd(t *testing.T) {
t.Run("batch add all false", func(t *testing.T) {
agg := NewResponseAggregator(RespAggLogicalOr, "")
results := map[string]AggregatorResErr{
"key1": {Result: false, Err: nil},
"key2": {Result: int64(0), Err: nil},
"key3": {Result: 0, Err: nil},
}
err := agg.BatchAdd(results)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
result, err := agg.Result()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if result != false {
t.Errorf("expected false, got %v", result)
}
})
t.Run("batch add with true", func(t *testing.T) {
agg := NewResponseAggregator(RespAggLogicalOr, "")
results := map[string]AggregatorResErr{
"key1": {Result: false, Err: nil},
"key2": {Result: true, Err: nil},
"key3": {Result: false, Err: nil},
}
err := agg.BatchAdd(results)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
result, err := agg.Result()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if result != true {
t.Errorf("expected true, got %v", result)
}
})
}

View File

@@ -48,15 +48,15 @@ PACKAGE_DIRS=$(find . -mindepth 2 -type f -name 'go.mod' -exec dirname {} \; \
for dir in $PACKAGE_DIRS
do
printf "${dir}: go get -u && go mod tidy\n"
#(cd ./${dir} && go get -u && go mod tidy -compat=1.18)
#(cd ./${dir} && go get -u && go mod tidy -compat=1.21)
done
for dir in $PACKAGE_DIRS
do
sed --in-place \
"s/redis\/go-redis\([^ ]*\) v.*/redis\/go-redis\1 ${TAG}/" "${dir}/go.mod"
#(cd ./${dir} && go get -u && go mod tidy -compat=1.18)
(cd ./${dir} && go mod tidy -compat=1.18)
#(cd ./${dir} && go get -u && go mod tidy -compat=1.21)
(cd ./${dir} && go mod tidy -compat=1.21)
done
sed --in-place "s/\(return \)\"[^\"]*\"/\1\"${TAG#v}\"/" ./version.go