1
0
mirror of https://github.com/redis/go-redis.git synced 2025-04-14 22:04:09 +03:00

feat: Enable CI for Redis CE 8.0 (#3274)

* chore: extract benchmark tests

* wip

* enable pubsub tests

* enable ring tests

* stop tests with build redis from source

* start all tests

* mix of makefile and action

* add sentinel configs

* fix example test

* stop debug on re

* wip

* enable gears for redis 7.2

* wip

* enable sentinel, they are expected to fail

* fix: linter configuration

* chore: update re versions

* return older redis enterprise version

* add basic codeql

* wip: increase timeout, focus only sentinel tests

* sentinels with docker network host

* enable all tests

* fix flanky test

* enable example tests

* tidy docker compose

* add debug output

* stop shutingdown masters

* don't test sentinel for re

* skip unsuported addscores

* Update README

bump go version in CI

* Update README.md

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update CONTRIBUTING.md

add information about new test setup

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Nedyalko Dyakov 2025-02-28 12:49:00 +02:00 committed by GitHub
parent 5314a57132
commit ebe11d06ca
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
60 changed files with 671 additions and 561 deletions

View File

@ -21,12 +21,7 @@ runs:
CLIENT_LIBS_TEST_IMAGE: "redislabs/client-libs-test:${{ inputs.redis-version }}"
run: |
set -e
redis_major_version=$(echo "$REDIS_VERSION" | grep -oP '^\d+')
if (( redis_major_version < 8 )); then
echo "Using redis-stack for module tests"
else
echo "Using redis CE for module tests"
fi
redis_version_np=$(echo "$REDIS_VERSION" | grep -oP '^\d+.\d+')
# Mapping of redis version to redis testing containers
declare -A redis_version_mapping=(
@ -36,27 +31,23 @@ runs:
)
if [[ -v redis_version_mapping[$REDIS_VERSION] ]]; then
echo "REDIS_MAJOR_VERSION=${redis_major_version}" >> $GITHUB_ENV
echo "REDIS_VERSION=${redis_version_np}" >> $GITHUB_ENV
echo "REDIS_IMAGE=redis:${{ inputs.redis-version }}" >> $GITHUB_ENV
echo "CLIENT_LIBS_TEST_IMAGE=redislabs/client-libs-test:${redis_version_mapping[$REDIS_VERSION]}" >> $GITHUB_ENV
else
echo "Version not found in the mapping."
exit 1
fi
sleep 10 # time to settle
sleep 10 # wait for redis to start
shell: bash
- name: Set up Docker Compose environment with redis ${{ inputs.redis-version }}
run: docker compose --profile all up -d
run: |
make docker.start
shell: bash
- name: Run tests
env:
RCE_DOCKER: "true"
RE_CLUSTER: "false"
run: |
go test \
--ginkgo.skip-file="ring_test.go" \
--ginkgo.skip-file="sentinel_test.go" \
--ginkgo.skip-file="pubsub_test.go" \
--ginkgo.skip-file="gears_commands_test.go" \
--ginkgo.label-filter="!NonRedisEnterprise"
make test.ci
shell: bash

View File

@ -10,13 +10,19 @@ permissions:
contents: read
jobs:
build:
name: build
benchmark:
name: benchmark
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
go-version: [1.21.x, 1.22.x, 1.23.x]
redis-version:
- "8.0-M03" # 8.0 milestone 4
- "7.4.2" # should use redis stack 7.4
go-version:
- "1.23.x"
- "1.24.x"
steps:
- name: Set up ${{ matrix.go-version }}
@ -27,15 +33,38 @@ jobs:
- name: Checkout code
uses: actions/checkout@v4
- name: Test
run: make test
- name: Setup Test environment
env:
REDIS_VERSION: ${{ matrix.redis-version }}
CLIENT_LIBS_TEST_IMAGE: "redislabs/client-libs-test:${{ matrix.redis-version }}"
run: |
set -e
redis_version_np=$(echo "$REDIS_VERSION" | grep -oP '^\d+.\d+')
# Mapping of redis version to redis testing containers
declare -A redis_version_mapping=(
["8.0-M03"]="8.0-M04-pre"
["7.4.2"]="rs-7.4.0-v2"
)
if [[ -v redis_version_mapping[$REDIS_VERSION] ]]; then
echo "REDIS_VERSION=${redis_version_np}" >> $GITHUB_ENV
echo "REDIS_IMAGE=redis:${{ matrix.redis-version }}" >> $GITHUB_ENV
echo "CLIENT_LIBS_TEST_IMAGE=redislabs/client-libs-test:${redis_version_mapping[$REDIS_VERSION]}" >> $GITHUB_ENV
else
echo "Version not found in the mapping."
exit 1
fi
shell: bash
- name: Set up Docker Compose environment with redis ${{ matrix.redis-version }}
run: make docker.start
shell: bash
- name: Benchmark Tests
env:
RCE_DOCKER: "true"
RE_CLUSTER: "false"
run: make bench
shell: bash
- name: Upload to Codecov
uses: codecov/codecov-action@v5
with:
files: coverage.txt
token: ${{ secrets.CODECOV_TOKEN }}
test-redis-ce:
name: test-redis-ce
runs-on: ubuntu-latest
@ -47,11 +76,10 @@ jobs:
- "7.4.2" # should use redis stack 7.4
- "7.2.7" # should redis stack 7.2
go-version:
- "1.22.x"
- "1.23.x"
- "1.24.x"
steps:
- name: Checkout code
uses: actions/checkout@v4
@ -60,4 +88,10 @@ jobs:
with:
go-version: ${{matrix.go-version}}
redis-version: ${{ matrix.redis-version }}
- name: Upload to Codecov
uses: codecov/codecov-action@v5
with:
files: coverage.txt
token: ${{ secrets.CODECOV_TOKEN }}

68
.github/workflows/codeql-analysis.yml vendored Normal file
View File

@ -0,0 +1,68 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'go' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Learn more about CodeQL language support at https://git.io/codeql-language-support
steps:
- name: Checkout repository
uses: actions/checkout@v4
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v3
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3

View File

@ -25,7 +25,7 @@ jobs:
strategy:
fail-fast: false
matrix:
go-version: [ "1.21", "1.22", "1.23" ]
go-version: ["1.24"]
steps:
- name: Set up ${{ matrix.go-version }}
@ -38,4 +38,4 @@ jobs:
- name: Test doc examples
working-directory: ./doctests
run: go test
run: go test -v

View File

@ -15,7 +15,7 @@ jobs:
strategy:
fail-fast: false
matrix:
go-version: [1.23.x]
go-version: [1.24.x]
re-build: ["7.4.2-54"]
steps:
@ -47,7 +47,7 @@ jobs:
- name: Test
env:
RE_CLUSTER: true
REDIS_MAJOR_VERSION: 7
REDIS_VERSION: "7.4"
run: |
go test \
--ginkgo.skip-file="ring_test.go" \

4
.gitignore vendored
View File

@ -5,4 +5,6 @@ testdata/*
*.tar.gz
*.dic
redis8tests.sh
.vscode
coverage.txt
**/coverage.txt
.vscode

View File

@ -32,20 +32,33 @@ Here's how to get started with your code contribution:
1. Create your own fork of go-redis
2. Do the changes in your fork
3. If you need a development environment, run `make test`. Note: this clones and builds the latest release of [redis](https://redis.io). You also need a redis-stack-server docker, in order to run the capabilities tests. This can be started by running:
```docker run -p 6379:6379 -it redis/redis-stack-server:edge```
4. While developing, make sure the tests pass by running `make tests`
3. If you need a development environment, run `make docker.start`.
> Note: this clones and builds the docker containers specified in `docker-compose.yml`, to understand more about
> the infrastructure that will be started you can check the `docker-compose.yml`. You also have the possiblity
> to specify the redis image that will be pulled with the env variable `CLIENT_LIBS_TEST_IMAGE`.
> By default the docker image that will be pulled and started is `redislabs/client-libs-test:rs-7.4.0-v2`.
> If you want to test with newer Redis version, using a newer version of `redislabs/client-libs-test` should work out of the box.
4. While developing, make sure the tests pass by running `make test` (if you have the docker containers running, `make test.ci` may be sufficient).
> Note: `make test` will try to start all containers, run the tests with `make test.ci` and then stop all containers.
5. If you like the change and think the project could use it, send a
pull request
To see what else is part of the automation, run `invoke -l`
## Testing
Call `make test` to run all tests, including linters.
### Setting up Docker
To run the tests, you need to have Docker installed and running. If you are using a host OS that does not support
docker host networks out of the box (e.g. Windows, OSX), you need to set up a docker desktop and enable docker host networks.
### Running tests
Call `make test` to run all tests.
Continuous Integration uses these same wrappers to run all of these
tests against multiple versions of python. Feel free to test your
tests against multiple versions of redis. Feel free to test your
changes against all the go versions supported, as declared by the
[build.yml](./.github/workflows/build.yml) file.

View File

@ -1,45 +1,35 @@
GO_MOD_DIRS := $(shell find . -type f -name 'go.mod' -exec dirname {} \; | sort)
export REDIS_MAJOR_VERSION := 7
test: testdeps
docker start go-redis-redis-stack || docker run -d --name go-redis-redis-stack -p 6379:6379 -e REDIS_ARGS="--enable-debug-command yes --enable-module-command yes" redis/redis-stack-server:latest
$(eval GO_VERSION := $(shell go version | cut -d " " -f 3 | cut -d. -f2))
docker.start:
docker compose --profile all up -d --quiet-pull
docker.stop:
docker compose --profile all down
test:
$(MAKE) docker.start
$(MAKE) test.ci
$(MAKE) docker.stop
test.ci:
set -e; for dir in $(GO_MOD_DIRS); do \
if echo "$${dir}" | grep -q "./example" && [ "$(GO_VERSION)" = "19" ]; then \
echo "Skipping go test in $${dir} due to Go version 1.19 and dir contains ./example"; \
continue; \
fi; \
echo "go test in $${dir}"; \
(cd "$${dir}" && \
go mod tidy -compat=1.18 && \
go test && \
go test ./... -short -race && \
go test ./... -run=NONE -bench=. -benchmem && \
env GOOS=linux GOARCH=386 go test && \
go test -coverprofile=coverage.txt -covermode=atomic ./... && \
go vet); \
go vet && \
go test -coverprofile=coverage.txt -covermode=atomic ./... -race); \
done
cd internal/customvet && go build .
go vet -vettool ./internal/customvet/customvet
docker stop go-redis-redis-stack
testdeps: testdata/redis/src/redis-server
bench: testdeps
bench:
go test ./... -test.run=NONE -test.bench=. -test.benchmem
.PHONY: all test testdeps bench fmt
.PHONY: all test bench fmt
build:
go build .
testdata/redis:
mkdir -p $@
wget -qO- https://download.redis.io/releases/redis-7.4.2.tar.gz | tar xvz --strip-components=1 -C $@
testdata/redis/src/redis-server: testdata/redis
cd $< && make all
fmt:
gofumpt -w ./
goimports -w -local github.com/redis/go-redis ./

View File

@ -14,6 +14,20 @@
> See [OpenTelemetry](https://github.com/redis/go-redis/tree/master/example/otel) example which
> demonstrates how you can use Uptrace to monitor go-redis.
## Supported versions
In `go-redis` we are aiming to support the last three releases of Redis. Currently, this means we do support:
- [Redis 7.2](https://raw.githubusercontent.com/redis/redis/7.2/00-RELEASENOTES) - using Redis Stack 7.2 for modules support
- [Redis 7.4](https://raw.githubusercontent.com/redis/redis/7.4/00-RELEASENOTES) - using Redis Stack 7.4 for modules support
- [Redis 8.0](https://raw.githubusercontent.com/redis/redis/8.0/00-RELEASENOTES) - using Redis CE 8.0 where modules are included
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
Redis Stack 7.2 and some commands are changed with Redis CE 8.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.
## How do I Redis?
[Learn for free at Redis University](https://university.redis.com/)

View File

@ -242,7 +242,7 @@ var _ = Describe("ACL permissions", Label("NonRedisEnterprise"), func() {
})
It("set permissions for module commands", func() {
SkipBeforeRedisMajor(8, "permissions for modules are supported for Redis Version >=8")
SkipBeforeRedisVersion(8, "permissions for modules are supported for Redis Version >=8")
Expect(client.FlushDB(ctx).Err()).NotTo(HaveOccurred())
val, err := client.FTCreate(ctx, "txt", &redis.FTCreateOptions{}, &redis.FieldSchema{FieldName: "txt", FieldType: redis.SearchFieldTypeText}).Result()
Expect(err).NotTo(HaveOccurred())
@ -322,7 +322,7 @@ var _ = Describe("ACL permissions", Label("NonRedisEnterprise"), func() {
})
It("set permissions for module categories", func() {
SkipBeforeRedisMajor(8, "permissions for modules are supported for Redis Version >=8")
SkipBeforeRedisVersion(8, "permissions for modules are supported for Redis Version >=8")
Expect(client.FlushDB(ctx).Err()).NotTo(HaveOccurred())
val, err := client.FTCreate(ctx, "txt", &redis.FTCreateOptions{}, &redis.FieldSchema{FieldName: "txt", FieldType: redis.SearchFieldTypeText}).Result()
Expect(err).NotTo(HaveOccurred())
@ -419,7 +419,7 @@ var _ = Describe("ACL Categories", func() {
})
It("lists acl categories and subcategories with Modules", func() {
SkipBeforeRedisMajor(8, "modules are included in acl for redis version >= 8")
SkipBeforeRedisVersion(8, "modules are included in acl for redis version >= 8")
aclTestCase := map[string]string{
"search": "FT.CREATE",
"bloom": "bf.add",

View File

@ -277,37 +277,41 @@ func BenchmarkXRead(b *testing.B) {
func newClusterScenario() *clusterScenario {
return &clusterScenario{
ports: []string{"16600", "16601", "16602", "16603", "16604", "16605"},
nodeIDs: make([]string, 6),
processes: make(map[string]*redisProcess, 6),
clients: make(map[string]*redis.Client, 6),
ports: []string{"16600", "16601", "16602", "16603", "16604", "16605"},
nodeIDs: make([]string, 6),
clients: make(map[string]*redis.Client, 6),
}
}
var clusterBench *clusterScenario
func BenchmarkClusterPing(b *testing.B) {
if testing.Short() {
b.Skip("skipping in short mode")
}
ctx := context.Background()
cluster := newClusterScenario()
if err := startCluster(ctx, cluster); err != nil {
b.Fatal(err)
if clusterBench == nil {
clusterBench = newClusterScenario()
if err := configureClusterTopology(ctx, clusterBench); err != nil {
b.Fatal(err)
}
}
defer cluster.Close()
client := cluster.newClusterClient(ctx, redisClusterOptions())
client := clusterBench.newClusterClient(ctx, redisClusterOptions())
defer client.Close()
b.ResetTimer()
b.Run("cluster ping", func(b *testing.B) {
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
err := client.Ping(ctx).Err()
if err != nil {
b.Fatal(err)
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
err := client.Ping(ctx).Err()
if err != nil {
b.Fatal(err)
}
}
}
})
})
}
@ -317,23 +321,26 @@ func BenchmarkClusterDoInt(b *testing.B) {
}
ctx := context.Background()
cluster := newClusterScenario()
if err := startCluster(ctx, cluster); err != nil {
b.Fatal(err)
if clusterBench == nil {
clusterBench = newClusterScenario()
if err := configureClusterTopology(ctx, clusterBench); err != nil {
b.Fatal(err)
}
}
defer cluster.Close()
client := cluster.newClusterClient(ctx, redisClusterOptions())
client := clusterBench.newClusterClient(ctx, redisClusterOptions())
defer client.Close()
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
err := client.Do(ctx, "SET", 10, 10).Err()
if err != nil {
b.Fatal(err)
b.Run("cluster do set int", func(b *testing.B) {
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
err := client.Do(ctx, "SET", 10, 10).Err()
if err != nil {
b.Fatal(err)
}
}
}
})
})
}
@ -343,26 +350,29 @@ func BenchmarkClusterSetString(b *testing.B) {
}
ctx := context.Background()
cluster := newClusterScenario()
if err := startCluster(ctx, cluster); err != nil {
b.Fatal(err)
if clusterBench == nil {
clusterBench = newClusterScenario()
if err := configureClusterTopology(ctx, clusterBench); err != nil {
b.Fatal(err)
}
}
defer cluster.Close()
client := cluster.newClusterClient(ctx, redisClusterOptions())
client := clusterBench.newClusterClient(ctx, redisClusterOptions())
defer client.Close()
value := string(bytes.Repeat([]byte{'1'}, 10000))
b.ResetTimer()
b.Run("cluster set string", func(b *testing.B) {
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
err := client.Set(ctx, "key", value, 0).Err()
if err != nil {
b.Fatal(err)
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
err := client.Set(ctx, "key", value, 0).Err()
if err != nil {
b.Fatal(err)
}
}
}
})
})
}
@ -372,21 +382,6 @@ func BenchmarkExecRingSetAddrsCmd(b *testing.B) {
ringShard2Name = "ringShardTwo"
)
for _, port := range []string{ringShard1Port, ringShard2Port} {
if _, err := startRedis(port); err != nil {
b.Fatal(err)
}
}
b.Cleanup(func() {
for _, p := range processes {
if err := p.Close(); err != nil {
b.Errorf("Failed to stop redis process: %v", err)
}
}
processes = nil
})
ring := redis.NewRing(&redis.RingOptions{
Addrs: map[string]string{
"ringShardOne": ":" + ringShard1Port,

View File

@ -4,6 +4,7 @@ import "context"
type ClusterCmdable interface {
ClusterMyShardID(ctx context.Context) *StringCmd
ClusterMyID(ctx context.Context) *StringCmd
ClusterSlots(ctx context.Context) *ClusterSlotsCmd
ClusterShards(ctx context.Context) *ClusterShardsCmd
ClusterLinks(ctx context.Context) *ClusterLinksCmd
@ -35,6 +36,12 @@ func (c cmdable) ClusterMyShardID(ctx context.Context) *StringCmd {
return cmd
}
func (c cmdable) ClusterMyID(ctx context.Context) *StringCmd {
cmd := NewStringCmd(ctx, "cluster", "myid")
_ = c(ctx, cmd)
return cmd
}
func (c cmdable) ClusterSlots(ctx context.Context) *ClusterSlotsCmd {
cmd := NewClusterSlotsCmd(ctx, "cluster", "slots")
_ = c(ctx, cmd)

View File

@ -194,6 +194,7 @@ var _ = Describe("Commands", func() {
})
It("should ClientKillByFilter with MAXAGE", Label("NonRedisEnterprise"), func() {
SkipBeforeRedisVersion(7.4, "doesn't work with older redis stack images")
var s []string
started := make(chan bool)
done := make(chan bool)
@ -211,18 +212,18 @@ var _ = Describe("Commands", func() {
select {
case <-done:
Fail("BLPOP is not blocked.")
case <-time.After(1 * time.Second):
case <-time.After(1100 * time.Millisecond):
// ok
}
killed := client.ClientKillByFilter(ctx, "MAXAGE", "1")
Expect(killed.Err()).NotTo(HaveOccurred())
Expect(killed.Val()).To(BeNumerically(">=", 2))
Expect(killed.Val()).To(BeNumerically(">=", 1))
select {
case <-done:
// ok
case <-time.After(time.Second):
case <-time.After(200 * time.Millisecond):
Fail("BLPOP is still blocked.")
}
})
@ -345,7 +346,7 @@ var _ = Describe("Commands", func() {
})
It("should ConfigGet Modules", func() {
SkipBeforeRedisMajor(8, "Config doesn't include modules before Redis 8")
SkipBeforeRedisVersion(8, "Config doesn't include modules before Redis 8")
expected := map[string]string{
"search-*": "search-timeout",
"ts-*": "ts-retention-policy",
@ -380,7 +381,7 @@ var _ = Describe("Commands", func() {
})
It("should ConfigGet with Modules", Label("NonRedisEnterprise"), func() {
SkipBeforeRedisMajor(8, "config get won't return modules configs before redis 8")
SkipBeforeRedisVersion(8, "config get won't return modules configs before redis 8")
configGet := client.ConfigGet(ctx, "*")
Expect(configGet.Err()).NotTo(HaveOccurred())
Expect(configGet.Val()).To(HaveKey("maxmemory"))
@ -391,7 +392,7 @@ var _ = Describe("Commands", func() {
})
It("should ConfigSet FT DIALECT", func() {
SkipBeforeRedisMajor(8, "config doesn't include modules before Redis 8")
SkipBeforeRedisVersion(8, "config doesn't include modules before Redis 8")
defaultState, err := client.ConfigGet(ctx, "search-default-dialect").Result()
Expect(err).NotTo(HaveOccurred())
@ -437,13 +438,13 @@ var _ = Describe("Commands", func() {
})
It("should ConfigSet fail for ReadOnly", func() {
SkipBeforeRedisMajor(8, "Config doesn't include modules before Redis 8")
SkipBeforeRedisVersion(8, "Config doesn't include modules before Redis 8")
_, err := client.ConfigSet(ctx, "search-max-doctablesize", "100000").Result()
Expect(err).To(HaveOccurred())
})
It("should ConfigSet Modules", func() {
SkipBeforeRedisMajor(8, "Config doesn't include modules before Redis 8")
SkipBeforeRedisVersion(8, "Config doesn't include modules before Redis 8")
defaults := map[string]string{}
expected := map[string]string{
"search-timeout": "100",
@ -484,7 +485,7 @@ var _ = Describe("Commands", func() {
})
It("should Fail ConfigSet Modules", func() {
SkipBeforeRedisMajor(8, "Config doesn't include modules before Redis 8")
SkipBeforeRedisVersion(8, "Config doesn't include modules before Redis 8")
expected := map[string]string{
"search-timeout": "-100",
"ts-retention-policy": "-10",
@ -533,7 +534,7 @@ var _ = Describe("Commands", func() {
})
It("should Info Modules", Label("redis.info"), func() {
SkipBeforeRedisMajor(8, "modules are included in info for Redis Version >= 8")
SkipBeforeRedisVersion(8, "modules are included in info for Redis Version >= 8")
info := client.Info(ctx)
Expect(info.Err()).NotTo(HaveOccurred())
Expect(info.Val()).NotTo(BeNil())
@ -558,7 +559,7 @@ var _ = Describe("Commands", func() {
})
It("should InfoMap Modules", Label("redis.info"), func() {
SkipBeforeRedisMajor(8, "modules are included in info for Redis Version >= 8")
SkipBeforeRedisVersion(8, "modules are included in info for Redis Version >= 8")
info := client.InfoMap(ctx)
Expect(info.Err()).NotTo(HaveOccurred())
Expect(info.Val()).NotTo(BeNil())
@ -701,8 +702,8 @@ var _ = Describe("Commands", func() {
})
})
Describe("debugging", func() {
PIt("should DebugObject", func() {
Describe("debugging", Label("NonRedisEnterprise"), func() {
It("should DebugObject", func() {
err := client.DebugObject(ctx, "foo").Err()
Expect(err).To(MatchError("ERR no such key"))
@ -1332,6 +1333,7 @@ var _ = Describe("Commands", func() {
})
It("should HScan without values", Label("NonRedisEnterprise"), func() {
SkipBeforeRedisVersion(7.4, "doesn't work with older redis stack images")
for i := 0; i < 1000; i++ {
sadd := client.HSet(ctx, "myhash", fmt.Sprintf("key%d", i), "hello")
Expect(sadd.Err()).NotTo(HaveOccurred())
@ -2625,6 +2627,7 @@ var _ = Describe("Commands", func() {
})
It("should HExpire", Label("hash-expiration", "NonRedisEnterprise"), func() {
SkipBeforeRedisVersion(7.4, "doesn't work with older redis stack images")
res, err := client.HExpire(ctx, "no_such_key", 10*time.Second, "field1", "field2", "field3").Result()
Expect(err).To(BeNil())
Expect(res).To(BeEquivalentTo([]int64{-2, -2, -2}))
@ -2640,6 +2643,7 @@ var _ = Describe("Commands", func() {
})
It("should HPExpire", Label("hash-expiration", "NonRedisEnterprise"), func() {
SkipBeforeRedisVersion(7.4, "doesn't work with older redis stack images")
res, err := client.HPExpire(ctx, "no_such_key", 10*time.Second, "field1", "field2", "field3").Result()
Expect(err).To(BeNil())
Expect(res).To(BeEquivalentTo([]int64{-2, -2, -2}))
@ -2655,6 +2659,7 @@ var _ = Describe("Commands", func() {
})
It("should HExpireAt", Label("hash-expiration", "NonRedisEnterprise"), func() {
SkipBeforeRedisVersion(7.4, "doesn't work with older redis stack images")
resEmpty, err := client.HExpireAt(ctx, "no_such_key", time.Now().Add(10*time.Second), "field1", "field2", "field3").Result()
Expect(err).To(BeNil())
Expect(resEmpty).To(BeEquivalentTo([]int64{-2, -2, -2}))
@ -2670,6 +2675,7 @@ var _ = Describe("Commands", func() {
})
It("should HPExpireAt", Label("hash-expiration", "NonRedisEnterprise"), func() {
SkipBeforeRedisVersion(7.4, "doesn't work with older redis stack images")
resEmpty, err := client.HPExpireAt(ctx, "no_such_key", time.Now().Add(10*time.Second), "field1", "field2", "field3").Result()
Expect(err).To(BeNil())
Expect(resEmpty).To(BeEquivalentTo([]int64{-2, -2, -2}))
@ -2685,6 +2691,7 @@ var _ = Describe("Commands", func() {
})
It("should HPersist", Label("hash-expiration", "NonRedisEnterprise"), func() {
SkipBeforeRedisVersion(7.4, "doesn't work with older redis stack images")
resEmpty, err := client.HPersist(ctx, "no_such_key", "field1", "field2", "field3").Result()
Expect(err).To(BeNil())
Expect(resEmpty).To(BeEquivalentTo([]int64{-2, -2, -2}))
@ -2708,6 +2715,7 @@ var _ = Describe("Commands", func() {
})
It("should HExpireTime", Label("hash-expiration", "NonRedisEnterprise"), func() {
SkipBeforeRedisVersion(7.4, "doesn't work with older redis stack images")
resEmpty, err := client.HExpireTime(ctx, "no_such_key", "field1", "field2", "field3").Result()
Expect(err).To(BeNil())
Expect(resEmpty).To(BeEquivalentTo([]int64{-2, -2, -2}))
@ -2727,6 +2735,7 @@ var _ = Describe("Commands", func() {
})
It("should HPExpireTime", Label("hash-expiration", "NonRedisEnterprise"), func() {
SkipBeforeRedisVersion(7.4, "doesn't work with older redis stack images")
resEmpty, err := client.HPExpireTime(ctx, "no_such_key", "field1", "field2", "field3").Result()
Expect(err).To(BeNil())
Expect(resEmpty).To(BeEquivalentTo([]int64{-2, -2, -2}))
@ -2747,6 +2756,7 @@ var _ = Describe("Commands", func() {
})
It("should HTTL", Label("hash-expiration", "NonRedisEnterprise"), func() {
SkipBeforeRedisVersion(7.4, "doesn't work with older redis stack images")
resEmpty, err := client.HTTL(ctx, "no_such_key", "field1", "field2", "field3").Result()
Expect(err).To(BeNil())
Expect(resEmpty).To(BeEquivalentTo([]int64{-2, -2, -2}))
@ -2766,6 +2776,7 @@ var _ = Describe("Commands", func() {
})
It("should HPTTL", Label("hash-expiration", "NonRedisEnterprise"), func() {
SkipBeforeRedisVersion(7.4, "doesn't work with older redis stack images")
resEmpty, err := client.HPTTL(ctx, "no_such_key", "field1", "field2", "field3").Result()
Expect(err).To(BeNil())
Expect(resEmpty).To(BeEquivalentTo([]int64{-2, -2, -2}))
@ -6040,6 +6051,7 @@ var _ = Describe("Commands", func() {
})
It("should XRead LastEntry", Label("NonRedisEnterprise"), func() {
SkipBeforeRedisVersion(7.4, "doesn't work with older redis stack images")
res, err := client.XRead(ctx, &redis.XReadArgs{
Streams: []string{"stream"},
Count: 2, // we expect 1 message
@ -6057,6 +6069,7 @@ var _ = Describe("Commands", func() {
})
It("should XRead LastEntry from two streams", Label("NonRedisEnterprise"), func() {
SkipBeforeRedisVersion(7.4, "doesn't work with older redis stack images")
res, err := client.XRead(ctx, &redis.XReadArgs{
Streams: []string{"stream", "stream"},
ID: "+",
@ -6079,6 +6092,7 @@ var _ = Describe("Commands", func() {
})
It("should XRead LastEntry blocks", Label("NonRedisEnterprise"), func() {
SkipBeforeRedisVersion(7.4, "doesn't work with older redis stack images")
start := time.Now()
go func() {
defer GinkgoRecover()
@ -6614,14 +6628,12 @@ var _ = Describe("Commands", func() {
res, err := client.ZRangeWithScores(ctx, "result", 0, -1).Result()
Expect(err).NotTo(HaveOccurred())
Expect(res).To(ContainElement(redis.Z{
Score: 190.44242984775784,
Member: "Palermo",
}))
Expect(res).To(ContainElement(redis.Z{
Score: 56.4412578701582,
Member: "Catania",
}))
Expect(len(res)).To(Equal(2))
var palermo, catania redis.Z
Expect(res).To(ContainElement(HaveField("Member", "Palermo"), &palermo))
Expect(res).To(ContainElement(HaveField("Member", "Catania"), &catania))
Expect(palermo.Score).To(BeNumerically("~", 190, 1))
Expect(catania.Score).To(BeNumerically("~", 56, 1))
})
It("should search geo radius with options", func() {
@ -6933,16 +6945,13 @@ var _ = Describe("Commands", func() {
v, err := client.ZRangeWithScores(ctx, "key2", 0, -1).Result()
Expect(err).NotTo(HaveOccurred())
Expect(v).To(Equal([]redis.Z{
{
Score: 56.441257870158204,
Member: "Catania",
},
{
Score: 190.44242984775784,
Member: "Palermo",
},
}))
Expect(len(v)).To(Equal(2))
var palermo, catania redis.Z
Expect(v).To(ContainElement(HaveField("Member", "Palermo"), &palermo))
Expect(v).To(ContainElement(HaveField("Member", "Catania"), &catania))
Expect(palermo.Score).To(BeNumerically("~", 190, 1))
Expect(catania.Score).To(BeNumerically("~", 56, 1))
})
})
@ -7332,6 +7341,7 @@ var _ = Describe("Commands", func() {
})
It("Shows function stats", func() {
SkipBeforeRedisVersion(7.4, "doesn't work with older redis stack images")
defer client.FunctionKill(ctx)
// We can not run blocking commands in Redis functions, so we're using an infinite loop,

View File

@ -2,7 +2,7 @@
services:
redis:
image: ${CLIENT_LIBS_TEST_IMAGE:-redislabs/client-libs-test:7.4.1}
image: ${CLIENT_LIBS_TEST_IMAGE:-redislabs/client-libs-test:rs-7.4.0-v2}
container_name: redis-standalone
environment:
- TLS_ENABLED=yes
@ -21,9 +21,9 @@ services:
- all-stack
- all
cluster:
image: ${CLIENT_LIBS_TEST_IMAGE:-redislabs/client-libs-test:7.4.1}
container_name: redis-cluster
osscluster:
image: ${CLIENT_LIBS_TEST_IMAGE:-redislabs/client-libs-test:rs-7.4.0-v2}
container_name: redis-osscluster
environment:
- NODES=6
- PORT=16600
@ -31,110 +31,71 @@ services:
ports:
- "16600-16605:16600-16605"
volumes:
- "./dockers/cluster:/redis/work"
- "./dockers/osscluster:/redis/work"
profiles:
- cluster
- all-stack
- all
sentinel-cluster:
image: ${CLIENT_LIBS_TEST_IMAGE:-redislabs/client-libs-test:rs-7.4.0-v2}
container_name: redis-sentinel-cluster
network_mode: "host"
environment:
- NODES=3
- TLS_ENABLED=yes
- REDIS_CLUSTER=no
- PORT=9121
command: ${REDIS_EXTRA_ARGS:---enable-debug-command yes --enable-module-command yes --tls-auth-clients optional --save ""}
#ports:
# - "9121-9123:9121-9123"
volumes:
- "./dockers/sentinel-cluster:/redis/work"
profiles:
- sentinel
- all-stack
- all
sentinel:
image: ${REDIS_IMAGE:-redis:7.4.1}
image: ${CLIENT_LIBS_TEST_IMAGE:-redislabs/client-libs-test:rs-7.4.0-v2}
container_name: redis-sentinel
depends_on:
- redis
entrypoint: "redis-sentinel /redis.conf --port 26379"
ports:
- 26379:26379
volumes:
- "./dockers/sentinel.conf:/redis.conf"
profiles:
- sentinel
- all-stack
- all
sentinel2:
image: ${REDIS_IMAGE:-redis:7.4.1}
container_name: redis-sentinel2
depends_on:
- redis
entrypoint: "redis-sentinel /redis.conf --port 26380"
ports:
- 26380:26380
volumes:
- "./dockers/sentinel.conf:/redis.conf"
profiles:
- sentinel
- all-stack
- all
sentinel3:
image: ${REDIS_IMAGE:-redis:7.4.1}
container_name: redis-sentinel3
depends_on:
- redis
entrypoint: "redis-sentinel /redis.conf --port 26381"
ports:
- 26381:26381
volumes:
- "./dockers/sentinel.conf:/redis.conf"
profiles:
- sentinel
- all-stack
- all
redisRing1:
image: ${CLIENT_LIBS_TEST_IMAGE:-redislabs/client-libs-test:7.4.1}
container_name: redis-ring-1
- sentinel-cluster
environment:
- NODES=3
- REDIS_CLUSTER=no
- PORT=26379
command: ${REDIS_EXTRA_ARGS:---sentinel}
network_mode: "host"
#ports:
# - 26379:26379
# - 26380:26380
# - 26381:26381
volumes:
- "./dockers/sentinel.conf:/redis/config-default/redis.conf"
- "./dockers/sentinel:/redis/work"
profiles:
- sentinel
- all-stack
- all
ring-cluster:
image: ${CLIENT_LIBS_TEST_IMAGE:-redislabs/client-libs-test:rs-7.4.0-v2}
container_name: redis-ring-cluster
environment:
- NODES=3
- TLS_ENABLED=yes
- REDIS_CLUSTER=no
- PORT=6390
command: ${REDIS_EXTRA_ARGS:---enable-debug-command yes --enable-module-command yes --tls-auth-clients optional --save ""}
ports:
- 6390:6390
volumes:
- "./dockers/ring1:/redis/work"
profiles:
- ring
- cluster
- sentinel
- all-stack
- all
redisRing2:
image: ${CLIENT_LIBS_TEST_IMAGE:-redislabs/client-libs-test:7.4.1}
container_name: redis-ring-2
environment:
- TLS_ENABLED=yes
- REDIS_CLUSTER=no
- PORT=6391
command: ${REDIS_EXTRA_ARGS:---enable-debug-command yes --enable-module-command yes --tls-auth-clients optional --save ""}
ports:
- 6391:6391
volumes:
- "./dockers/ring2:/redis/work"
profiles:
- ring
- cluster
- sentinel
- all-stack
- all
redisRing3:
image: ${CLIENT_LIBS_TEST_IMAGE:-redislabs/client-libs-test:7.4.1}
container_name: redis-ring-3
environment:
- TLS_ENABLED=yes
- REDIS_CLUSTER=no
- PORT=6392
command: ${REDIS_EXTRA_ARGS:---enable-debug-command yes --enable-module-command yes --tls-auth-clients optional --save ""}
ports:
- 6392:6392
volumes:
- "./dockers/ring3:/redis/work"
- "./dockers/ring:/redis/work"
profiles:
- ring
- cluster
- sentinel
- all-stack
- all
- all

7
dockers/.gitignore vendored
View File

@ -1 +1,6 @@
*/
osscluster/
ring/
standalone/
sentinel-cluster/
sentinel/

View File

@ -1,5 +1,5 @@
sentinel resolve-hostnames yes
sentinel monitor go-redis-test redis 6379 2
sentinel monitor go-redis-test 127.0.0.1 9121 2
sentinel down-after-milliseconds go-redis-test 5000
sentinel failover-timeout go-redis-test 60000
sentinel parallel-syncs go-redis-test 1
sentinel parallel-syncs go-redis-test 1

View File

@ -21,6 +21,8 @@ func ExampleClient_bloom() {
})
// REMOVE_START
// make sure we are working with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "bikes:models")
// REMOVE_END

View File

@ -21,6 +21,8 @@ func ExampleClient_bf() {
})
// REMOVE_START
// make sure we are working with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "bike:1:stats")
// REMOVE_END

View File

@ -21,6 +21,8 @@ func ExampleClient_ping() {
})
// REMOVE_START
// make sure we are working with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "pings:2024-01-01-00:00")
// REMOVE_END
@ -66,6 +68,8 @@ func ExampleClient_bitcount() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
_, err := rdb.SetBit(ctx, "pings:2024-01-01-00:00", 123, 1).Result()
if err != nil {

View File

@ -23,6 +23,8 @@ func ExampleClient_del_cmd() {
})
// REMOVE_START
// make sure we are working with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "key1", "key2", "key3")
// REMOVE_END
@ -68,6 +70,8 @@ func ExampleClient_expire_cmd() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "mykey")
// REMOVE_END
@ -167,6 +171,8 @@ func ExampleClient_ttl_cmd() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "mykey")
// REMOVE_END

View File

@ -22,6 +22,8 @@ func ExampleClient_hset() {
})
// REMOVE_START
// make sure we are working with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "myhash")
// REMOVE_END
@ -112,6 +114,8 @@ func ExampleClient_hget() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "myhash")
// REMOVE_END
@ -157,6 +161,8 @@ func ExampleClient_hgetall() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "myhash")
// REMOVE_END
@ -209,6 +215,8 @@ func ExampleClient_hvals() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "myhash")
// REMOVE_END

View File

@ -22,6 +22,8 @@ func ExampleClient_cmd_flushall() {
// STEP_START flushall
// REMOVE_START
// make sure we are working with fresh database
rdb.FlushDB(ctx)
rdb.Set(ctx, "testkey1", "1", 0)
rdb.Set(ctx, "testkey2", "2", 0)
rdb.Set(ctx, "testkey3", "3", 0)

View File

@ -21,6 +21,8 @@ func ExampleClient_zadd_cmd() {
})
// REMOVE_START
// make sure we are working with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "myzset")
// REMOVE_END
@ -82,6 +84,8 @@ func ExampleClient_zrange1() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "myzset")
// REMOVE_END
@ -140,6 +144,8 @@ func ExampleClient_zrange2() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "myzset")
// REMOVE_END
@ -180,6 +186,8 @@ func ExampleClient_zrange3() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "myzset")
// REMOVE_END

View File

@ -21,6 +21,8 @@ func ExampleClient_cmd_incr() {
})
// REMOVE_START
// make sure we are working with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "mykey")
// REMOVE_END

View File

@ -21,6 +21,8 @@ func ExampleClient_cms() {
})
// REMOVE_START
// make sure we are working with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "bikes:profit")
// REMOVE_END

View File

@ -21,6 +21,8 @@ func ExampleClient_cuckoo() {
})
// REMOVE_START
// make sure we are working with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "bikes:models")
// REMOVE_END

View File

@ -20,7 +20,10 @@ func ExampleClient_geoindex() {
DB: 0, // use default DB
Protocol: 2,
})
// REMOVE_START
// make sure we are working with fresh database
rdb.FlushDB(ctx)
rdb.FTDropIndex(ctx, "productidx")
rdb.FTDropIndex(ctx, "geomidx")
rdb.Del(ctx, "product:46885", "product:46886", "shape:1", "shape:2", "shape:3", "shape:4")

View File

@ -21,6 +21,8 @@ func ExampleClient_geoadd() {
})
// REMOVE_START
// make sure we are working with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "bikes:rentable")
// REMOVE_END
@ -81,6 +83,8 @@ func ExampleClient_geosearch() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "bikes:rentable")
_, err := rdb.GeoAdd(ctx, "bikes:rentable",

View File

@ -21,6 +21,8 @@ func ExampleClient_set_get_all() {
})
// REMOVE_START
// make sure we are working with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "bike:1")
// REMOVE_END
@ -102,6 +104,8 @@ func ExampleClient_hmget() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "bike:1")
// REMOVE_END
@ -160,6 +164,8 @@ func ExampleClient_hincrby() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "bike:1")
// REMOVE_END
@ -209,6 +215,8 @@ func ExampleClient_incrby_get_mget() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "bike:1:stats")
// REMOVE_END

View File

@ -21,6 +21,8 @@ func ExampleClient_pfadd() {
})
// REMOVE_START
// make sure we are working with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "bikes", "commuter_bikes", "all_bikes")
// REMOVE_END

View File

@ -26,6 +26,8 @@ func ExampleClient_search_json() {
})
// STEP_END
// REMOVE_START
// make sure we are working with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "user:1", "user:2", "user:3")
rdb.FTDropIndex(ctx, "idx:users")
// REMOVE_END

View File

@ -20,6 +20,8 @@ func ExampleClient_setget() {
})
// REMOVE_START
// make sure we are working with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "bike")
// REMOVE_END
@ -67,6 +69,8 @@ func ExampleClient_str() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "bike")
// REMOVE_END
@ -120,6 +124,8 @@ func ExampleClient_num() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "crashes")
// REMOVE_END
@ -174,6 +180,8 @@ func ExampleClient_arr() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "newbike")
// REMOVE_END

View File

@ -21,6 +21,8 @@ func ExampleClient_queue() {
})
// REMOVE_START
// make sure we are working with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "bikes:repairs")
// REMOVE_END
@ -75,6 +77,8 @@ func ExampleClient_stack() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "bikes:repairs")
// REMOVE_END
@ -129,6 +133,8 @@ func ExampleClient_llen() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "bikes:repairs")
// REMOVE_END
@ -156,6 +162,8 @@ func ExampleClient_lmove_lrange() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "bikes:repairs")
rdb.Del(ctx, "bikes:finished")
// REMOVE_END
@ -220,6 +228,8 @@ func ExampleClient_lpush_rpush() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "bikes:repairs")
// REMOVE_END
@ -274,6 +284,8 @@ func ExampleClient_variadic() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "bikes:repairs")
// REMOVE_END
@ -319,6 +331,8 @@ func ExampleClient_lpop_rpop() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "bikes:repairs")
// REMOVE_END
@ -384,6 +398,8 @@ func ExampleClient_ltrim() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "bikes:repairs")
// REMOVE_END
@ -429,6 +445,8 @@ func ExampleClient_ltrim_end_of_list() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "bikes:repairs")
// REMOVE_END
@ -474,6 +492,8 @@ func ExampleClient_brpop() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "bikes:repairs")
// REMOVE_END
@ -529,6 +549,8 @@ func ExampleClient_rule1() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "new_bikes")
// REMOVE_END

View File

@ -20,6 +20,8 @@ func ExampleClient_transactions() {
DB: 0, // use default DB
})
// REMOVE_START
// make sure we are working with fresh database
rdb.FlushDB(ctx)
for i := 0; i < 5; i++ {
rdb.Del(ctx, fmt.Sprintf("seat:%d", i))
}

View File

@ -21,6 +21,8 @@ func ExampleClient_query_agg() {
})
// HIDE_END
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.FTDropIndex(ctx, "idx:bicycle")
rdb.FTDropIndex(ctx, "idx:email")
// REMOVE_END

View File

@ -5,6 +5,8 @@ package example_commands_test
import (
"context"
"fmt"
"slices"
"strings"
"github.com/redis/go-redis/v9"
)
@ -21,6 +23,8 @@ func ExampleClient_query_em() {
// HIDE_END
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.FTDropIndex(ctx, "idx:bicycle")
rdb.FTDropIndex(ctx, "idx:email")
// REMOVE_END
@ -274,11 +278,16 @@ func ExampleClient_query_em() {
fmt.Println(res3.Total) // >>> 5
for _, doc := range res3.Docs {
docs := res3.Docs
slices.SortFunc(docs, func(a, b redis.Document) int {
return strings.Compare(a.ID, b.ID)
})
for _, doc := range docs {
fmt.Println(doc.ID)
}
// >>> bicycle:5
// >>> bicycle:0
// >>> bicycle:5
// >>> bicycle:6
// >>> bicycle:7
// >>> bicycle:8
@ -350,8 +359,8 @@ func ExampleClient_query_em() {
// 1
// bicycle:0
// 5
// bicycle:5
// bicycle:0
// bicycle:5
// bicycle:6
// bicycle:7
// bicycle:8

View File

@ -21,6 +21,8 @@ func ExampleClient_query_ft() {
})
// HIDE_END
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.FTDropIndex(ctx, "idx:bicycle")
rdb.FTDropIndex(ctx, "idx:email")
// REMOVE_END

View File

@ -21,6 +21,8 @@ func ExampleClient_query_geo() {
})
// HIDE_END
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.FTDropIndex(ctx, "idx:bicycle")
// REMOVE_END

View File

@ -21,6 +21,8 @@ func ExampleClient_Set_and_get() {
// HIDE_END
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
errFlush := rdb.FlushDB(ctx).Err() // Clear the database before each test
if errFlush != nil {
panic(errFlush)

View File

@ -21,6 +21,8 @@ func ExampleClient_sadd() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "bikes:racing:france")
rdb.Del(ctx, "bikes:racing:usa")
// REMOVE_END
@ -76,6 +78,8 @@ func ExampleClient_sismember() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "bikes:racing:france")
rdb.Del(ctx, "bikes:racing:usa")
// REMOVE_END
@ -125,6 +129,8 @@ func ExampleClient_sinter() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "bikes:racing:france")
rdb.Del(ctx, "bikes:racing:usa")
// REMOVE_END
@ -165,6 +171,8 @@ func ExampleClient_scard() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "bikes:racing:france")
// REMOVE_END
@ -198,6 +206,8 @@ func ExampleClient_saddsmembers() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "bikes:racing:france")
// REMOVE_END
@ -216,7 +226,7 @@ func ExampleClient_saddsmembers() {
panic(err)
}
// Sort the strings in the slice to make sure the output is lexicographical
// Sort the strings in the slice to make sure the output is lexicographical
sort.Strings(res10)
fmt.Println(res10) // >>> [bike:1 bike:2 bike:3]
@ -237,6 +247,8 @@ func ExampleClient_smismember() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "bikes:racing:france")
// REMOVE_END
@ -279,6 +291,8 @@ func ExampleClient_sdiff() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "bikes:racing:france")
rdb.Del(ctx, "bikes:racing:usa")
// REMOVE_END
@ -298,8 +312,7 @@ func ExampleClient_sdiff() {
panic(err)
}
// Sort the strings in the slice to make sure the output is lexicographical
// Sort the strings in the slice to make sure the output is lexicographical
sort.Strings(res13)
fmt.Println(res13) // >>> [bike:2 bike:3]
@ -319,6 +332,8 @@ func ExampleClient_multisets() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "bikes:racing:france")
rdb.Del(ctx, "bikes:racing:usa")
rdb.Del(ctx, "bikes:racing:italy")
@ -357,7 +372,7 @@ func ExampleClient_multisets() {
panic(err)
}
// Sort the strings in the slice to make sure the output is lexicographical
// Sort the strings in the slice to make sure the output is lexicographical
sort.Strings(res15)
fmt.Println(res15) // >>> [bike:1 bike:2 bike:3 bike:4]
@ -384,7 +399,7 @@ func ExampleClient_multisets() {
panic(err)
}
// Sort the strings in the slice to make sure the output is lexicographical
// Sort the strings in the slice to make sure the output is lexicographical
sort.Strings(res18)
fmt.Println(res18) // >>> [bike:2 bike:3]
@ -408,6 +423,8 @@ func ExampleClient_srem() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "bikes:racing:france")
// REMOVE_END

View File

@ -20,6 +20,8 @@ func ExampleClient_zadd() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "racer_scores")
// REMOVE_END
@ -76,6 +78,8 @@ func ExampleClient_zrange() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "racer_scores")
// REMOVE_END
@ -127,6 +131,8 @@ func ExampleClient_zrangewithscores() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "racer_scores")
// REMOVE_END
@ -168,6 +174,8 @@ func ExampleClient_zrangebyscore() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "racer_scores")
// REMOVE_END
@ -211,6 +219,8 @@ func ExampleClient_zremrangebyscore() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "racer_scores")
// REMOVE_END
@ -270,6 +280,8 @@ func ExampleClient_zrank() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "racer_scores")
// REMOVE_END
@ -316,6 +328,8 @@ func ExampleClient_zaddlex() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "racer_scores")
// REMOVE_END
@ -377,6 +391,8 @@ func ExampleClient_leaderboard() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "racer_scores")
// REMOVE_END

View File

@ -26,6 +26,8 @@ func ExampleClient_xadd() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "race:france")
// REMOVE_END
@ -105,6 +107,8 @@ func ExampleClient_racefrance1() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "race:france")
// REMOVE_END
@ -227,6 +231,8 @@ func ExampleClient_raceusa() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "race:usa")
// REMOVE_END
@ -310,6 +316,8 @@ func ExampleClient_racefrance2() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "race:france")
// REMOVE_END
@ -478,6 +486,8 @@ func ExampleClient_xgroupcreate() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "race:france")
// REMOVE_END
@ -520,6 +530,8 @@ func ExampleClient_xgroupcreatemkstream() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "race:italy")
// REMOVE_END
@ -549,6 +561,8 @@ func ExampleClient_xgroupread() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "race:italy")
// REMOVE_END
@ -654,6 +668,8 @@ func ExampleClient_raceitaly() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "race:italy")
rdb.XGroupDestroy(ctx, "race:italy", "italy_riders")
// REMOVE_END
@ -1011,6 +1027,8 @@ func ExampleClient_xdel() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "race:italy")
// REMOVE_END

View File

@ -20,6 +20,8 @@ func ExampleClient_set_get() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "bike:1")
// REMOVE_END
@ -56,6 +58,8 @@ func ExampleClient_setnx_xx() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Set(ctx, "bike:1", "Deimos", 0)
// REMOVE_END
@ -101,6 +105,8 @@ func ExampleClient_mset() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "bike:1", "bike:2", "bike:3")
// REMOVE_END
@ -137,6 +143,8 @@ func ExampleClient_incr() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "total_crashes")
// REMOVE_END

View File

@ -21,6 +21,8 @@ func ExampleClient_tdigstart() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "racer_ages", "bikes:sales")
// REMOVE_END
@ -69,6 +71,8 @@ func ExampleClient_tdigcdf() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "racer_ages", "bikes:sales")
// REMOVE_END
@ -126,6 +130,8 @@ func ExampleClient_tdigquant() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "racer_ages")
// REMOVE_END
@ -177,6 +183,8 @@ func ExampleClient_tdigmin() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "racer_ages")
// REMOVE_END
@ -228,6 +236,8 @@ func ExampleClient_tdigreset() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "racer_ages")
// REMOVE_END
_, err := rdb.TDigestCreate(ctx, "racer_ages").Result()

View File

@ -21,6 +21,8 @@ func ExampleClient_topk() {
})
// REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "bikes:keywords")
// REMOVE_END

View File

@ -10,6 +10,6 @@ require (
)
require (
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
)

View File

@ -34,6 +34,7 @@ func libCodeWithConfig(libName string) string {
return fmt.Sprintf(lib, libName)
}
// TODO: Drop Gears
var _ = Describe("RedisGears commands", Label("gears"), func() {
ctx := context.TODO()
var client *redis.Client
@ -49,6 +50,7 @@ var _ = Describe("RedisGears commands", Label("gears"), func() {
})
It("should TFunctionLoad, TFunctionLoadArgs and TFunctionDelete ", Label("gears", "tfunctionload"), func() {
SkipAfterRedisVersion(7.4, "gears are not working in later versions")
resultAdd, err := client.TFunctionLoad(ctx, libCode("lib1")).Result()
Expect(err).NotTo(HaveOccurred())
Expect(resultAdd).To(BeEquivalentTo("OK"))
@ -58,6 +60,7 @@ var _ = Describe("RedisGears commands", Label("gears"), func() {
Expect(resultAdd).To(BeEquivalentTo("OK"))
})
It("should TFunctionList", Label("gears", "tfunctionlist"), func() {
SkipAfterRedisVersion(7.4, "gears are not working in later versions")
resultAdd, err := client.TFunctionLoad(ctx, libCode("lib1")).Result()
Expect(err).NotTo(HaveOccurred())
Expect(resultAdd).To(BeEquivalentTo("OK"))
@ -71,6 +74,7 @@ var _ = Describe("RedisGears commands", Label("gears"), func() {
})
It("should TFCall", Label("gears", "tfcall"), func() {
SkipAfterRedisVersion(7.4, "gears are not working in later versions")
var resultAdd interface{}
resultAdd, err := client.TFunctionLoad(ctx, libCode("lib1")).Result()
Expect(err).NotTo(HaveOccurred())
@ -81,6 +85,7 @@ var _ = Describe("RedisGears commands", Label("gears"), func() {
})
It("should TFCallArgs", Label("gears", "tfcallargs"), func() {
SkipAfterRedisVersion(7.4, "gears are not working in later versions")
var resultAdd interface{}
resultAdd, err := client.TFunctionLoad(ctx, libCode("lib1")).Result()
Expect(err).NotTo(HaveOccurred())
@ -92,6 +97,7 @@ var _ = Describe("RedisGears commands", Label("gears"), func() {
})
It("should TFCallASYNC", Label("gears", "TFCallASYNC"), func() {
SkipAfterRedisVersion(7.4, "gears are not working in later versions")
var resultAdd interface{}
resultAdd, err := client.TFunctionLoad(ctx, libCode("lib1")).Result()
Expect(err).NotTo(HaveOccurred())
@ -102,6 +108,7 @@ var _ = Describe("RedisGears commands", Label("gears"), func() {
})
It("should TFCallASYNCArgs", Label("gears", "TFCallASYNCargs"), func() {
SkipAfterRedisVersion(7.4, "gears are not working in later versions")
var resultAdd interface{}
resultAdd, err := client.TFunctionLoad(ctx, libCode("lib1")).Result()
Expect(err).NotTo(HaveOccurred())

View File

@ -292,8 +292,8 @@ var _ = Describe("race", func() {
BeforeEach(func() {
C, N = 10, 1000
if testing.Short() {
C = 4
N = 100
C = 2
N = 50
}
})

View File

@ -85,6 +85,7 @@ var _ = Describe("ScanIterator", func() {
})
It("should hscan across multiple pages", func() {
SkipBeforeRedisVersion(7.4, "doesn't work with older redis stack images")
Expect(hashSeed(71)).NotTo(HaveOccurred())
var vals []string
@ -100,6 +101,7 @@ var _ = Describe("ScanIterator", func() {
})
It("should hscan without values across multiple pages", Label("NonRedisEnterprise"), func() {
SkipBeforeRedisVersion(7.4, "doesn't work with older redis stack images")
Expect(hashSeed(71)).NotTo(HaveOccurred())
var vals []string

View File

@ -4,9 +4,8 @@ import (
"fmt"
"net"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"sync"
"testing"
"time"
@ -28,12 +27,12 @@ const (
const (
sentinelName = "go-redis-test"
sentinelMasterPort = "9123"
sentinelSlave1Port = "9124"
sentinelSlave2Port = "9125"
sentinelPort1 = "9126"
sentinelPort2 = "9127"
sentinelPort3 = "9128"
sentinelMasterPort = "9121"
sentinelSlave1Port = "9122"
sentinelSlave2Port = "9123"
sentinelPort1 = "26379"
sentinelPort2 = "26380"
sentinelPort3 = "26381"
)
var (
@ -49,19 +48,15 @@ var (
var (
sentinelAddrs = []string{":" + sentinelPort1, ":" + sentinelPort2, ":" + sentinelPort3}
processes map[string]*redisProcess
redisMain *redisProcess
ringShard1, ringShard2, ringShard3 *redisProcess
sentinelMaster, sentinelSlave1, sentinelSlave2 *redisProcess
sentinel1, sentinel2, sentinel3 *redisProcess
ringShard1, ringShard2, ringShard3 *redis.Client
sentinelMaster, sentinelSlave1, sentinelSlave2 *redis.Client
sentinel1, sentinel2, sentinel3 *redis.Client
)
var cluster = &clusterScenario{
ports: []string{"16600", "16601", "16602", "16603", "16604", "16605"},
nodeIDs: make([]string, 6),
processes: make(map[string]*redisProcess, 6),
clients: make(map[string]*redis.Client, 6),
ports: []string{"16600", "16601", "16602", "16603", "16604", "16605"},
nodeIDs: make([]string, 6),
clients: make(map[string]*redis.Client, 6),
}
// Redis Software Cluster
@ -70,30 +65,23 @@ var RECluster = false
// Redis Community Edition Docker
var RCEDocker = false
// Notes the major version of redis we are executing tests.
// Notes version of redis we are executing tests against.
// This can be used before we change the bsm fork of ginkgo for one,
// which have support for label sets, so we can filter tests per redis major version.
var RedisMajorVersion = 7
// which have support for label sets, so we can filter tests per redis version.
var RedisVersion float64 = 7.2
func SkipBeforeRedisMajor(version int, msg string) {
if RedisMajorVersion < version {
Skip(fmt.Sprintf("(redis major version < %d) %s", version, msg))
func SkipBeforeRedisVersion(version float64, msg string) {
if RedisVersion < version {
Skip(fmt.Sprintf("(redis version < %f) %s", version, msg))
}
}
func SkipAfterRedisMajor(version int, msg string) {
if RedisMajorVersion > version {
Skip(fmt.Sprintf("(redis major version > %d) %s", version, msg))
func SkipAfterRedisVersion(version float64, msg string) {
if RedisVersion > version {
Skip(fmt.Sprintf("(redis version > %f) %s", version, msg))
}
}
func registerProcess(port string, p *redisProcess) {
if processes == nil {
processes = make(map[string]*redisProcess)
}
processes[port] = p
}
var _ = BeforeSuite(func() {
addr := os.Getenv("REDIS_PORT")
if addr != "" {
@ -104,35 +92,33 @@ var _ = BeforeSuite(func() {
RECluster, _ = strconv.ParseBool(os.Getenv("RE_CLUSTER"))
RCEDocker, _ = strconv.ParseBool(os.Getenv("RCE_DOCKER"))
RedisMajorVersion, _ = strconv.Atoi(os.Getenv("REDIS_MAJOR_VERSION"))
RedisVersion, _ = strconv.ParseFloat(strings.Trim(os.Getenv("REDIS_VERSION"), "\""), 64)
if RedisMajorVersion == 0 {
RedisMajorVersion = 7
if RedisVersion == 0 {
RedisVersion = 7.2
}
fmt.Printf("RECluster: %v\n", RECluster)
fmt.Printf("RCEDocker: %v\n", RCEDocker)
fmt.Printf("REDIS_MAJOR_VERSION: %v\n", RedisMajorVersion)
fmt.Printf("REDIS_VERSION: %v\n", RedisVersion)
if RedisMajorVersion < 6 || RedisMajorVersion > 8 {
panic("incorrect or not supported redis major version")
if RedisVersion < 7.0 || RedisVersion > 9 {
panic("incorrect or not supported redis version")
}
if !RECluster && !RCEDocker {
redisMain, err = startRedis(redisPort)
redisPort = redisStackPort
redisAddr = redisStackAddr
if !RECluster {
ringShard1, err = connectTo(ringShard1Port)
Expect(err).NotTo(HaveOccurred())
ringShard1, err = startRedis(ringShard1Port)
ringShard2, err = connectTo(ringShard2Port)
Expect(err).NotTo(HaveOccurred())
ringShard2, err = startRedis(ringShard2Port)
ringShard3, err = connectTo(ringShard3Port)
Expect(err).NotTo(HaveOccurred())
ringShard3, err = startRedis(ringShard3Port)
Expect(err).NotTo(HaveOccurred())
sentinelMaster, err = startRedis(sentinelMasterPort)
sentinelMaster, err = connectTo(sentinelMasterPort)
Expect(err).NotTo(HaveOccurred())
sentinel1, err = startSentinel(sentinelPort1, sentinelName, sentinelMasterPort)
@ -144,24 +130,20 @@ var _ = BeforeSuite(func() {
sentinel3, err = startSentinel(sentinelPort3, sentinelName, sentinelMasterPort)
Expect(err).NotTo(HaveOccurred())
sentinelSlave1, err = startRedis(
sentinelSlave1Port, "--slaveof", "127.0.0.1", sentinelMasterPort)
sentinelSlave1, err = connectTo(sentinelSlave1Port)
Expect(err).NotTo(HaveOccurred())
sentinelSlave2, err = startRedis(
sentinelSlave2Port, "--slaveof", "127.0.0.1", sentinelMasterPort)
err = sentinelSlave1.SlaveOf(ctx, "127.0.0.1", sentinelMasterPort).Err()
Expect(err).NotTo(HaveOccurred())
err = startCluster(ctx, cluster)
sentinelSlave2, err = connectTo(sentinelSlave2Port)
Expect(err).NotTo(HaveOccurred())
} else {
redisPort = redisStackPort
redisAddr = redisStackAddr
if !RECluster {
// populate cluster node information
Expect(configureClusterTopology(ctx, cluster)).NotTo(HaveOccurred())
}
err = sentinelSlave2.SlaveOf(ctx, "127.0.0.1", sentinelMasterPort).Err()
Expect(err).NotTo(HaveOccurred())
// populate cluster node information
Expect(configureClusterTopology(ctx, cluster)).NotTo(HaveOccurred())
}
})
@ -169,12 +151,6 @@ var _ = AfterSuite(func() {
if !RECluster {
Expect(cluster.Close()).NotTo(HaveOccurred())
}
// NOOP if there are no processes registered
for _, p := range processes {
Expect(p.Close()).NotTo(HaveOccurred())
}
processes = nil
})
func TestGinkgoSuite(t *testing.T) {
@ -204,7 +180,7 @@ func redisOptions() *redis.Options {
}
return &redis.Options{
Addr: redisAddr,
DB: 15,
DB: 0,
DialTimeout: 10 * time.Second,
ReadTimeout: 30 * time.Second,
@ -256,7 +232,9 @@ func performAsync(n int, cbs ...func(int)) *sync.WaitGroup {
var wg sync.WaitGroup
for _, cb := range cbs {
wg.Add(n)
for i := 0; i < n; i++ {
// start from 1, so we can skip db 0 where such test is executed with
// select db command
for i := 1; i <= n; i++ {
go func(cb func(int), i int) {
defer GinkgoRecover()
defer wg.Done()
@ -313,15 +291,6 @@ func eventually(fn func() error, timeout time.Duration) error {
}
}
func execCmd(name string, args ...string) (*os.Process, error) {
cmd := exec.Command(name, args...)
if testing.Verbose() {
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
}
return cmd.Process, cmd.Start()
}
func connectTo(port string) (*redis.Client, error) {
client := redis.NewClient(&redis.Options{
Addr: ":" + port,
@ -338,117 +307,22 @@ func connectTo(port string) (*redis.Client, error) {
return client, nil
}
type redisProcess struct {
*os.Process
*redis.Client
}
func (p *redisProcess) Close() error {
if err := p.Kill(); err != nil {
return err
}
err := eventually(func() error {
if err := p.Client.Ping(ctx).Err(); err != nil {
return nil
}
return fmt.Errorf("client %s is not shutdown", p.Options().Addr)
}, 10*time.Second)
if err != nil {
return err
}
p.Client.Close()
return nil
}
var (
redisServerBin, _ = filepath.Abs(filepath.Join("testdata", "redis", "src", "redis-server"))
redisServerConf, _ = filepath.Abs(filepath.Join("testdata", "redis", "redis.conf"))
redisSentinelConf, _ = filepath.Abs(filepath.Join("testdata", "redis", "sentinel.conf"))
)
func redisDir(port string) (string, error) {
dir, err := filepath.Abs(filepath.Join("testdata", "instances", port))
if err != nil {
return "", err
}
if err := os.RemoveAll(dir); err != nil {
return "", err
}
if err := os.MkdirAll(dir, 0o775); err != nil {
return "", err
}
return dir, nil
}
func startRedis(port string, args ...string) (*redisProcess, error) {
dir, err := redisDir(port)
if err != nil {
return nil, err
}
if err := exec.Command("cp", "-f", redisServerConf, dir).Run(); err != nil {
return nil, err
}
baseArgs := []string{filepath.Join(dir, "redis.conf"), "--port", port, "--dir", dir, "--enable-module-command", "yes"}
process, err := execCmd(redisServerBin, append(baseArgs, args...)...)
if err != nil {
return nil, err
}
func startSentinel(port, masterName, masterPort string) (*redis.Client, error) {
client, err := connectTo(port)
if err != nil {
process.Kill()
return nil, err
}
p := &redisProcess{process, client}
registerProcess(port, p)
return p, nil
}
func startSentinel(port, masterName, masterPort string) (*redisProcess, error) {
dir, err := redisDir(port)
if err != nil {
return nil, err
}
sentinelConf := filepath.Join(dir, "sentinel.conf")
if err := os.WriteFile(sentinelConf, nil, 0o644); err != nil {
return nil, err
}
process, err := execCmd(redisServerBin, sentinelConf, "--sentinel", "--port", port, "--dir", dir)
if err != nil {
return nil, err
}
client, err := connectTo(port)
if err != nil {
process.Kill()
return nil, err
}
// set down-after-milliseconds=2000
// link: https://github.com/redis/redis/issues/8607
for _, cmd := range []*redis.StatusCmd{
redis.NewStatusCmd(ctx, "SENTINEL", "MONITOR", masterName, "127.0.0.1", masterPort, "2"),
redis.NewStatusCmd(ctx, "SENTINEL", "SET", masterName, "down-after-milliseconds", "2000"),
redis.NewStatusCmd(ctx, "SENTINEL", "SET", masterName, "failover-timeout", "1000"),
redis.NewStatusCmd(ctx, "SENTINEL", "SET", masterName, "parallel-syncs", "1"),
} {
client.Process(ctx, cmd)
if err := cmd.Err(); err != nil {
process.Kill()
if err := cmd.Err(); err != nil && !strings.Contains(err.Error(), "ERR Duplicate master name.") {
return nil, fmt.Errorf("%s failed: %w", cmd, err)
}
}
p := &redisProcess{process, client}
registerProcess(port, p)
return p, nil
return client, nil
}
//------------------------------------------------------------------------------

View File

@ -6,6 +6,7 @@ import (
"errors"
"fmt"
"net"
"slices"
"strconv"
"strings"
"sync"
@ -19,10 +20,9 @@ import (
)
type clusterScenario struct {
ports []string
nodeIDs []string
processes map[string]*redisProcess
clients map[string]*redis.Client
ports []string
nodeIDs []string
clients map[string]*redis.Client
}
func (s *clusterScenario) slots() []int {
@ -101,20 +101,17 @@ func (s *clusterScenario) Close() error {
}
}
for _, port := range s.ports {
if process, ok := processes[port]; ok {
if process != nil {
process.Close()
}
delete(processes, port)
}
}
return nil
}
func configureClusterTopology(ctx context.Context, scenario *clusterScenario) error {
allowErrs := []string{
"ERR Slot 0 is already busy",
"ERR Slot 5461 is already busy",
"ERR Slot 10923 is already busy",
"ERR Slot 16384 is already busy",
}
err := collectNodeInformation(ctx, scenario)
if err != nil {
return err
@ -131,7 +128,7 @@ func configureClusterTopology(ctx context.Context, scenario *clusterScenario) er
slots := scenario.slots()
for pos, master := range scenario.masters() {
err := master.ClusterAddSlotsRange(ctx, slots[pos], slots[pos+1]-1).Err()
if err != nil {
if err != nil && slices.Contains(allowErrs, err.Error()) == false {
return err
}
}
@ -199,7 +196,7 @@ func configureClusterTopology(ctx context.Context, scenario *clusterScenario) er
return err
}
return assertSlotsEqual(res, wanted)
}, 60*time.Second)
}, 90*time.Second)
if err != nil {
return err
}
@ -214,31 +211,17 @@ func collectNodeInformation(ctx context.Context, scenario *clusterScenario) erro
Addr: ":" + port,
})
info, err := client.ClusterNodes(ctx).Result()
myID, err := client.ClusterMyID(ctx).Result()
if err != nil {
return err
}
scenario.clients[port] = client
scenario.nodeIDs[pos] = info[:40]
scenario.nodeIDs[pos] = myID
}
return nil
}
// startCluster start a cluster
func startCluster(ctx context.Context, scenario *clusterScenario) error {
// Start processes and collect node ids
for _, port := range scenario.ports {
process, err := startRedis(port, "--cluster-enabled", "yes")
if err != nil {
return err
}
scenario.processes[port] = process
}
return configureClusterTopology(ctx, scenario)
}
func assertSlotsEqual(slots, wanted []redis.ClusterSlot) error {
outerLoop:
for _, s2 := range wanted {

View File

@ -89,6 +89,9 @@ var _ = Describe("PubSub", func() {
pubsub := client.Subscribe(ctx, "mychannel", "mychannel2")
defer pubsub.Close()
// sleep a bit to make sure redis knows about the subscriptions
time.Sleep(10 * time.Millisecond)
channels, err = client.PubSubChannels(ctx, "mychannel*").Result()
Expect(err).NotTo(HaveOccurred())
Expect(channels).To(ConsistOf([]string{"mychannel", "mychannel2"}))
@ -135,6 +138,8 @@ var _ = Describe("PubSub", func() {
pubsub := client.Subscribe(ctx, "mychannel", "mychannel2")
defer pubsub.Close()
// sleep a bit to make sure redis knows about the subscriptions
time.Sleep(10 * time.Millisecond)
channels, err := client.PubSubNumSub(ctx, "mychannel", "mychannel2", "mychannel3").Result()
Expect(err).NotTo(HaveOccurred())
Expect(channels).To(Equal(map[string]int64{
@ -152,6 +157,8 @@ var _ = Describe("PubSub", func() {
pubsub := client.PSubscribe(ctx, "*")
defer pubsub.Close()
// sleep a bit to make sure redis knows about the subscriptions
time.Sleep(10 * time.Millisecond)
num, err = client.PubSubNumPat(ctx).Result()
Expect(err).NotTo(HaveOccurred())
Expect(num).To(Equal(int64(1)))

View File

@ -105,7 +105,7 @@ var _ = Describe("races", func() {
})
It("should handle big vals in Get", func() {
C, N = 4, 100
C, N := 4, 100
bigVal := bigVal()
@ -126,7 +126,7 @@ var _ = Describe("races", func() {
})
It("should handle big vals in Set", func() {
C, N = 4, 100
C, N := 4, 100
bigVal := bigVal()
perform(C, func(id int) {
@ -138,7 +138,7 @@ var _ = Describe("races", func() {
})
It("should select db", Label("NonRedisEnterprise"), func() {
err := client.Set(ctx, "db", 1, 0).Err()
err := client.Set(ctx, "db", 0, 0).Err()
Expect(err).NotTo(HaveOccurred())
perform(C, func(id int) {
@ -159,7 +159,7 @@ var _ = Describe("races", func() {
n, err := client.Get(ctx, "db").Int64()
Expect(err).NotTo(HaveOccurred())
Expect(n).To(Equal(int64(1)))
Expect(n).To(Equal(int64(0)))
})
It("should select DB with read timeout", func() {
@ -214,12 +214,14 @@ var _ = Describe("races", func() {
Expect(val).To(Equal(int64(C * N)))
})
PIt("should BLPop", func() {
It("should BLPop", func() {
C := 5
N := 5
var received uint32
wg := performAsync(C, func(id int) {
for {
v, err := client.BLPop(ctx, 5*time.Second, "list").Result()
v, err := client.BLPop(ctx, time.Second, "list").Result()
if err != nil {
if err == redis.Nil {
break

View File

@ -66,11 +66,7 @@ var _ = Describe("Client", func() {
})
It("should Stringer", func() {
if RECluster {
Expect(client.String()).To(Equal(fmt.Sprintf("Redis<:%s db:0>", redisPort)))
} else {
Expect(client.String()).To(Equal(fmt.Sprintf("Redis<:%s db:15>", redisPort)))
}
Expect(client.String()).To(Equal(fmt.Sprintf("Redis<:%s db:0>", redisPort)))
})
It("supports context", func() {

View File

@ -130,34 +130,6 @@ var _ = Describe("Redis Ring", func() {
Expect(ringShard2.Info(ctx, "keyspace").Val()).To(ContainSubstring("keys=44"))
})
It("uses single shard when one of the shards is down", func() {
// Stop ringShard2.
Expect(ringShard2.Close()).NotTo(HaveOccurred())
Eventually(func() int {
return ring.Len()
}, "30s").Should(Equal(1))
setRingKeys()
// RingShard1 should have all keys.
Expect(ringShard1.Info(ctx, "keyspace").Val()).To(ContainSubstring("keys=100"))
// Start ringShard2.
var err error
ringShard2, err = startRedis(ringShard2Port)
Expect(err).NotTo(HaveOccurred())
Eventually(func() int {
return ring.Len()
}, "30s").Should(Equal(2))
setRingKeys()
// RingShard2 should have its keys.
Expect(ringShard2.Info(ctx, "keyspace").Val()).To(ContainSubstring("keys=44"))
})
It("supports hash tags", func() {
for i := 0; i < 100; i++ {
err := ring.Set(ctx, fmt.Sprintf("key%d{tag}", i), "value", 0).Err()

View File

@ -2079,6 +2079,7 @@ func (c cmdable) FTTagVals(ctx context.Context, index string, field string) *Str
return cmd
}
// TODO: remove FTProfile
// type FTProfileResult struct {
// Results []interface{}
// Profile ProfileDetails

View File

@ -381,7 +381,7 @@ var _ = Describe("RediSearch commands Resp 2", Label("search"), func() {
// up until redis 8 the default scorer was TFIDF, in redis 8 it is BM25
// this test expect redis major version >= 8
It("should FTSearch WithScores", Label("search", "ftsearch"), func() {
SkipBeforeRedisMajor(8, "default scorer is not BM25")
SkipBeforeRedisVersion(7.9, "default scorer is not BM25")
text1 := &redis.FieldSchema{FieldName: "description", FieldType: redis.SearchFieldTypeText}
val, err := client.FTCreate(ctx, "idx1", &redis.FTCreateOptions{}, text1).Result()
@ -422,9 +422,9 @@ var _ = Describe("RediSearch commands Resp 2", Label("search"), func() {
})
// up until redis 8 the default scorer was TFIDF, in redis 8 it is BM25
// this test expect redis major version <=7
// this test expect redis version < 8.0
It("should FTSearch WithScores", Label("search", "ftsearch"), func() {
SkipAfterRedisMajor(7, "default scorer is not TFIDF")
SkipAfterRedisVersion(7.9, "default scorer is not TFIDF")
text1 := &redis.FieldSchema{FieldName: "description", FieldType: redis.SearchFieldTypeText}
val, err := client.FTCreate(ctx, "idx1", &redis.FTCreateOptions{}, text1).Result()
Expect(err).NotTo(HaveOccurred())
@ -464,17 +464,17 @@ var _ = Describe("RediSearch commands Resp 2", Label("search"), func() {
})
It("should FTConfigSet and FTConfigGet ", Label("search", "ftconfigget", "ftconfigset", "NonRedisEnterprise"), func() {
val, err := client.FTConfigSet(ctx, "TIMEOUT", "100").Result()
val, err := client.FTConfigSet(ctx, "MINPREFIX", "1").Result()
Expect(err).NotTo(HaveOccurred())
Expect(val).To(BeEquivalentTo("OK"))
res, err := client.FTConfigGet(ctx, "*").Result()
Expect(err).NotTo(HaveOccurred())
Expect(res["TIMEOUT"]).To(BeEquivalentTo("100"))
Expect(res["MINPREFIX"]).To(BeEquivalentTo("1"))
res, err = client.FTConfigGet(ctx, "TIMEOUT").Result()
res, err = client.FTConfigGet(ctx, "MINPREFIX").Result()
Expect(err).NotTo(HaveOccurred())
Expect(res).To(BeEquivalentTo(map[string]interface{}{"TIMEOUT": "100"}))
Expect(res).To(BeEquivalentTo(map[string]interface{}{"MINPREFIX": "1"}))
})
@ -667,6 +667,7 @@ var _ = Describe("RediSearch commands Resp 2", Label("search"), func() {
})
It("should FTAggregate with scorer and addscores", Label("search", "ftaggregate", "NonRedisEnterprise"), func() {
SkipBeforeRedisVersion(7.4, "no addscores support")
title := &redis.FieldSchema{FieldName: "title", FieldType: redis.SearchFieldTypeText, Sortable: false}
description := &redis.FieldSchema{FieldName: "description", FieldType: redis.SearchFieldTypeText, Sortable: false}
val, err := client.FTCreate(ctx, "idx1", &redis.FTCreateOptions{OnHash: true, Prefix: []interface{}{"product:"}}, title, description).Result()
@ -1273,6 +1274,7 @@ var _ = Describe("RediSearch commands Resp 2", Label("search"), func() {
})
It("should test dialect 4", Label("search", "ftcreate", "ftsearch", "NonRedisEnterprise"), func() {
SkipBeforeRedisVersion(7.4, "doesn't work with older redis stack images")
val, err := client.FTCreate(ctx, "idx1", &redis.FTCreateOptions{
Prefix: []interface{}{"resource:"},
}, &redis.FieldSchema{
@ -1405,6 +1407,7 @@ var _ = Describe("RediSearch commands Resp 2", Label("search"), func() {
})
It("should create search index with FLOAT16 and BFLOAT16 vectors", Label("search", "ftcreate", "NonRedisEnterprise"), func() {
SkipBeforeRedisVersion(7.4, "doesn't work with older redis stack images")
val, err := client.FTCreate(ctx, "index", &redis.FTCreateOptions{},
&redis.FieldSchema{FieldName: "float16", FieldType: redis.SearchFieldTypeVector, VectorArgs: &redis.FTVectorArgs{FlatOptions: &redis.FTFlatOptions{Type: "FLOAT16", Dim: 768, DistanceMetric: "COSINE"}}},
&redis.FieldSchema{FieldName: "bfloat16", FieldType: redis.SearchFieldTypeVector, VectorArgs: &redis.FTVectorArgs{FlatOptions: &redis.FTFlatOptions{Type: "BFLOAT16", Dim: 768, DistanceMetric: "COSINE"}}},
@ -1415,6 +1418,7 @@ var _ = Describe("RediSearch commands Resp 2", Label("search"), func() {
})
It("should test geoshapes query intersects and disjoint", Label("NonRedisEnterprise"), func() {
SkipBeforeRedisVersion(7.4, "doesn't work with older redis stack images")
_, err := client.FTCreate(ctx, "idx1", &redis.FTCreateOptions{}, &redis.FieldSchema{
FieldName: "g",
FieldType: redis.SearchFieldTypeGeoShape,
@ -1483,6 +1487,7 @@ var _ = Describe("RediSearch commands Resp 2", Label("search"), func() {
})
It("should search missing fields", Label("search", "ftcreate", "ftsearch", "NonRedisEnterprise"), func() {
SkipBeforeRedisVersion(7.4, "doesn't work with older redis stack images")
val, err := client.FTCreate(ctx, "idx1", &redis.FTCreateOptions{Prefix: []interface{}{"property:"}},
&redis.FieldSchema{FieldName: "title", FieldType: redis.SearchFieldTypeText, Sortable: true},
&redis.FieldSchema{FieldName: "features", FieldType: redis.SearchFieldTypeTag, IndexMissing: true},
@ -1527,6 +1532,7 @@ var _ = Describe("RediSearch commands Resp 2", Label("search"), func() {
})
It("should search empty fields", Label("search", "ftcreate", "ftsearch", "NonRedisEnterprise"), func() {
SkipBeforeRedisVersion(7.4, "doesn't work with older redis stack images")
val, err := client.FTCreate(ctx, "idx1", &redis.FTCreateOptions{Prefix: []interface{}{"property:"}},
&redis.FieldSchema{FieldName: "title", FieldType: redis.SearchFieldTypeText, Sortable: true},
&redis.FieldSchema{FieldName: "features", FieldType: redis.SearchFieldTypeTag, IndexEmpty: true},
@ -1687,18 +1693,18 @@ var _ = Describe("RediSearch FT.Config with Resp2 and Resp3", Label("search", "N
Expect(clientResp3.Close()).NotTo(HaveOccurred())
})
It("should FTConfigSet and FTConfigGet ", Label("search", "ftconfigget", "ftconfigset", "NonRedisEnterprise"), func() {
val, err := clientResp3.FTConfigSet(ctx, "TIMEOUT", "100").Result()
It("should FTConfigSet and FTConfigGet with resp2 and resp3", Label("search", "ftconfigget", "ftconfigset", "NonRedisEnterprise"), func() {
val, err := clientResp3.FTConfigSet(ctx, "MINPREFIX", "1").Result()
Expect(err).NotTo(HaveOccurred())
Expect(val).To(BeEquivalentTo("OK"))
res2, err := clientResp2.FTConfigGet(ctx, "TIMEOUT").Result()
res2, err := clientResp2.FTConfigGet(ctx, "MINPREFIX").Result()
Expect(err).NotTo(HaveOccurred())
Expect(res2).To(BeEquivalentTo(map[string]interface{}{"TIMEOUT": "100"}))
Expect(res2).To(BeEquivalentTo(map[string]interface{}{"MINPREFIX": "1"}))
res3, err := clientResp3.FTConfigGet(ctx, "TIMEOUT").Result()
res3, err := clientResp3.FTConfigGet(ctx, "MINPREFIX").Result()
Expect(err).NotTo(HaveOccurred())
Expect(res3).To(BeEquivalentTo(map[string]interface{}{"TIMEOUT": "100"}))
Expect(res3).To(BeEquivalentTo(map[string]interface{}{"MINPREFIX": "1"}))
})
It("should FTConfigGet all resp2 and resp3", Label("search", "NonRedisEnterprise"), func() {

View File

@ -6,13 +6,11 @@ import (
. "github.com/bsm/ginkgo/v2"
. "github.com/bsm/gomega"
"github.com/redis/go-redis/v9"
)
var _ = Describe("Sentinel PROTO 2", func() {
var client *redis.Client
BeforeEach(func() {
client = redis.NewFailoverClient(&redis.FailoverOptions{
MasterName: sentinelName,
@ -37,7 +35,6 @@ var _ = Describe("Sentinel PROTO 2", func() {
var _ = Describe("Sentinel", func() {
var client *redis.Client
var master *redis.Client
var masterPort string
var sentinel *redis.SentinelClient
BeforeEach(func() {
@ -61,18 +58,17 @@ var _ = Describe("Sentinel", func() {
Addr: net.JoinHostPort(addr[0], addr[1]),
MaxRetries: -1,
})
masterPort = addr[1]
// Wait until slaves are picked up by sentinel.
Eventually(func() string {
return sentinel1.Info(ctx).Val()
}, "15s", "100ms").Should(ContainSubstring("slaves=2"))
}, "20s", "100ms").Should(ContainSubstring("slaves=2"))
Eventually(func() string {
return sentinel2.Info(ctx).Val()
}, "15s", "100ms").Should(ContainSubstring("slaves=2"))
}, "20s", "100ms").Should(ContainSubstring("slaves=2"))
Eventually(func() string {
return sentinel3.Info(ctx).Val()
}, "15s", "100ms").Should(ContainSubstring("slaves=2"))
}, "20s", "100ms").Should(ContainSubstring("slaves=2"))
})
AfterEach(func() {
@ -96,7 +92,7 @@ var _ = Describe("Sentinel", func() {
Eventually(func() []string {
slavesAddr = redis.GetSlavesAddrByName(ctx, sentinel, sentinelName)
return slavesAddr
}, "15s", "100ms").Should(HaveLen(2))
}, "20s", "50ms").Should(HaveLen(2))
Eventually(func() bool {
sync := true
for _, addr := range slavesAddr {
@ -108,36 +104,35 @@ var _ = Describe("Sentinel", func() {
_ = slave.Close()
}
return sync
}, "15s", "100ms").Should(BeTrue())
}, "20s", "50ms").Should(BeTrue())
// Create subscription.
pub := client.Subscribe(ctx, "foo")
ch := pub.Channel()
// Kill master.
err = master.Shutdown(ctx).Err()
Expect(err).NotTo(HaveOccurred())
Eventually(func() error {
return master.Ping(ctx).Err()
}, "15s", "100ms").Should(HaveOccurred())
/*
err = master.Shutdown(ctx).Err()
Expect(err).NotTo(HaveOccurred())
Eventually(func() error {
return master.Ping(ctx).Err()
}, "20s", "50ms").Should(HaveOccurred())
*/
// Check that client picked up new master.
Eventually(func() string {
return client.Get(ctx, "foo").Val()
}, "15s", "100ms").Should(Equal("master"))
}, "20s", "100ms").Should(Equal("master"))
// Check if subscription is renewed.
var msg *redis.Message
Eventually(func() <-chan *redis.Message {
_ = client.Publish(ctx, "foo", "hello").Err()
return ch
}, "15s", "100ms").Should(Receive(&msg))
}, "20s", "100ms").Should(Receive(&msg))
Expect(msg.Channel).To(Equal("foo"))
Expect(msg.Payload).To(Equal("hello"))
Expect(pub.Close()).NotTo(HaveOccurred())
_, err = startRedis(masterPort)
Expect(err).NotTo(HaveOccurred())
})
It("supports DB selection", func() {
@ -197,7 +192,6 @@ var _ = Describe("NewFailoverClusterClient PROTO 2", func() {
var _ = Describe("NewFailoverClusterClient", func() {
var client *redis.ClusterClient
var master *redis.Client
var masterPort string
BeforeEach(func() {
client = redis.NewFailoverClusterClient(&redis.FailoverOptions{
@ -221,18 +215,17 @@ var _ = Describe("NewFailoverClusterClient", func() {
Addr: net.JoinHostPort(addr[0], addr[1]),
MaxRetries: -1,
})
masterPort = addr[1]
// Wait until slaves are picked up by sentinel.
Eventually(func() string {
return sentinel1.Info(ctx).Val()
}, "15s", "100ms").Should(ContainSubstring("slaves=2"))
}, "20s", "100ms").Should(ContainSubstring("slaves=2"))
Eventually(func() string {
return sentinel2.Info(ctx).Val()
}, "15s", "100ms").Should(ContainSubstring("slaves=2"))
}, "20s", "100ms").Should(ContainSubstring("slaves=2"))
Eventually(func() string {
return sentinel3.Info(ctx).Val()
}, "15s", "100ms").Should(ContainSubstring("slaves=2"))
}, "20s", "100ms").Should(ContainSubstring("slaves=2"))
})
AfterEach(func() {
@ -241,7 +234,6 @@ var _ = Describe("NewFailoverClusterClient", func() {
})
It("should facilitate failover", func() {
Skip("Flaky Test")
// Set value.
err := client.Set(ctx, "foo", "master", 0).Err()
Expect(err).NotTo(HaveOccurred())
@ -250,7 +242,7 @@ var _ = Describe("NewFailoverClusterClient", func() {
// Verify.
Eventually(func() string {
return client.Get(ctx, "foo").Val()
}, "15s", "1ms").Should(Equal("master"))
}, "20s", "1ms").Should(Equal("master"))
}
// Create subscription.
@ -258,33 +250,32 @@ var _ = Describe("NewFailoverClusterClient", func() {
ch := sub.Channel()
// Kill master.
err = master.Shutdown(ctx).Err()
Expect(err).NotTo(HaveOccurred())
Eventually(func() error {
return master.Ping(ctx).Err()
}, "15s", "100ms").Should(HaveOccurred())
/*
err = master.Shutdown(ctx).Err()
Expect(err).NotTo(HaveOccurred())
Eventually(func() error {
return master.Ping(ctx).Err()
}, "20s", "100ms").Should(HaveOccurred())
*/
// Check that client picked up new master.
Eventually(func() string {
return client.Get(ctx, "foo").Val()
}, "15s", "100ms").Should(Equal("master"))
}, "20s", "100ms").Should(Equal("master"))
// Check if subscription is renewed.
var msg *redis.Message
Eventually(func() <-chan *redis.Message {
_ = client.Publish(ctx, "foo", "hello").Err()
return ch
}, "15s", "100ms").Should(Receive(&msg))
}, "20s", "100ms").Should(Receive(&msg))
Expect(msg.Channel).To(Equal("foo"))
Expect(msg.Payload).To(Equal("hello"))
Expect(sub.Close()).NotTo(HaveOccurred())
_, err = startRedis(masterPort)
Expect(err).NotTo(HaveOccurred())
})
It("should sentinel cluster client setname", func() {
Skip("Flaky Test")
err := client.ForEachShard(ctx, func(ctx context.Context, c *redis.Client) error {
return c.Ping(ctx).Err()
})
@ -299,7 +290,6 @@ var _ = Describe("NewFailoverClusterClient", func() {
})
It("should sentinel cluster PROTO 3", func() {
Skip("Flaky Test")
_ = client.ForEachShard(ctx, func(ctx context.Context, c *redis.Client) error {
val, err := client.Do(ctx, "HELLO").Result()
Expect(err).NotTo(HaveOccurred())
@ -317,8 +307,8 @@ var _ = Describe("SentinelAclAuth", func() {
var client *redis.Client
var sentinel *redis.SentinelClient
sentinels := func() []*redisProcess {
return []*redisProcess{sentinel1, sentinel2, sentinel3}
sentinels := func() []*redis.Client {
return []*redis.Client{sentinel1, sentinel2, sentinel3}
}
BeforeEach(func() {
@ -328,7 +318,7 @@ var _ = Describe("SentinelAclAuth", func() {
"+sentinel|myid", "+sentinel|replicas", "+sentinel|sentinels")
for _, process := range sentinels() {
err := process.Client.Process(ctx, authCmd)
err := process.Process(ctx, authCmd)
Expect(err).NotTo(HaveOccurred())
}
@ -356,7 +346,7 @@ var _ = Describe("SentinelAclAuth", func() {
for _, process := range sentinels() {
Eventually(func() string {
return process.Info(ctx).Val()
}, "15s", "100ms").Should(ContainSubstring("sentinels=3"))
}, "20s", "100ms").Should(ContainSubstring("sentinels=3"))
}
})
@ -364,7 +354,7 @@ var _ = Describe("SentinelAclAuth", func() {
unauthCommand := redis.NewStatusCmd(ctx, "ACL", "DELUSER", aclSentinelUsername)
for _, process := range sentinels() {
err := process.Client.Process(ctx, unauthCommand)
err := process.Process(ctx, unauthCommand)
Expect(err).NotTo(HaveOccurred())
}

View File

@ -43,6 +43,7 @@ var _ = Describe("RedisTimeseries commands", Label("timeseries"), func() {
})
It("should TSCreate and TSCreateWithArgs", Label("timeseries", "tscreate", "tscreateWithArgs", "NonRedisEnterprise"), func() {
SkipBeforeRedisVersion(7.4, "older redis stack has different results for timeseries module")
result, err := client.TSCreate(ctx, "1").Result()
Expect(err).NotTo(HaveOccurred())
Expect(result).To(BeEquivalentTo("OK"))
@ -139,6 +140,7 @@ var _ = Describe("RedisTimeseries commands", Label("timeseries"), func() {
{Timestamp: 1013, Value: 10.0}}))
})
It("should TSAdd and TSAddWithArgs", Label("timeseries", "tsadd", "tsaddWithArgs", "NonRedisEnterprise"), func() {
SkipBeforeRedisVersion(7.4, "older redis stack has different results for timeseries module")
result, err := client.TSAdd(ctx, "1", 1, 1).Result()
Expect(err).NotTo(HaveOccurred())
Expect(result).To(BeEquivalentTo(1))
@ -232,6 +234,7 @@ var _ = Describe("RedisTimeseries commands", Label("timeseries"), func() {
})
It("should TSAlter", Label("timeseries", "tsalter", "NonRedisEnterprise"), func() {
SkipBeforeRedisVersion(7.4, "older redis stack has different results for timeseries module")
result, err := client.TSCreate(ctx, "1").Result()
Expect(err).NotTo(HaveOccurred())
Expect(result).To(BeEquivalentTo("OK"))
@ -349,6 +352,7 @@ var _ = Describe("RedisTimeseries commands", Label("timeseries"), func() {
})
It("should TSIncrBy, TSIncrByWithArgs, TSDecrBy and TSDecrByWithArgs", Label("timeseries", "tsincrby", "tsdecrby", "tsincrbyWithArgs", "tsdecrbyWithArgs", "NonRedisEnterprise"), func() {
SkipBeforeRedisVersion(7.4, "older redis stack has different results for timeseries module")
for i := 0; i < 100; i++ {
_, err := client.TSIncrBy(ctx, "1", 1).Result()
Expect(err).NotTo(HaveOccurred())

View File

@ -16,8 +16,7 @@ var _ = Describe("UniversalClient", func() {
}
})
It("should connect to failover servers", func() {
Skip("Flaky Test")
It("should connect to failover servers", Label("NonRedisEnterprise"), func() {
client = redis.NewUniversalClient(&redis.UniversalOptions{
MasterName: sentinelName,
Addrs: sentinelAddrs,