1
0
mirror of https://github.com/redis/go-redis.git synced 2025-04-17 20:17:02 +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 }}" CLIENT_LIBS_TEST_IMAGE: "redislabs/client-libs-test:${{ inputs.redis-version }}"
run: | run: |
set -e set -e
redis_major_version=$(echo "$REDIS_VERSION" | grep -oP '^\d+') redis_version_np=$(echo "$REDIS_VERSION" | grep -oP '^\d+.\d+')
if (( redis_major_version < 8 )); then
echo "Using redis-stack for module tests"
else
echo "Using redis CE for module tests"
fi
# Mapping of redis version to redis testing containers # Mapping of redis version to redis testing containers
declare -A redis_version_mapping=( declare -A redis_version_mapping=(
@ -36,27 +31,23 @@ runs:
) )
if [[ -v redis_version_mapping[$REDIS_VERSION] ]]; then 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 "REDIS_IMAGE=redis:${{ inputs.redis-version }}" >> $GITHUB_ENV
echo "CLIENT_LIBS_TEST_IMAGE=redislabs/client-libs-test:${redis_version_mapping[$REDIS_VERSION]}" >> $GITHUB_ENV echo "CLIENT_LIBS_TEST_IMAGE=redislabs/client-libs-test:${redis_version_mapping[$REDIS_VERSION]}" >> $GITHUB_ENV
else else
echo "Version not found in the mapping." echo "Version not found in the mapping."
exit 1 exit 1
fi fi
sleep 10 # time to settle sleep 10 # wait for redis to start
shell: bash shell: bash
- name: Set up Docker Compose environment with redis ${{ inputs.redis-version }} - name: Set up Docker Compose environment with redis ${{ inputs.redis-version }}
run: docker compose --profile all up -d run: |
make docker.start
shell: bash shell: bash
- name: Run tests - name: Run tests
env: env:
RCE_DOCKER: "true" RCE_DOCKER: "true"
RE_CLUSTER: "false" RE_CLUSTER: "false"
run: | run: |
go test \ make test.ci
--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"
shell: bash shell: bash

View File

@ -10,13 +10,19 @@ permissions:
contents: read contents: read
jobs: jobs:
build:
name: build benchmark:
name: benchmark
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
fail-fast: false fail-fast: false
matrix: 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: steps:
- name: Set up ${{ matrix.go-version }} - name: Set up ${{ matrix.go-version }}
@ -27,14 +33,37 @@ jobs:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Test - name: Setup Test environment
run: make test 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+')
- name: Upload to Codecov # Mapping of redis version to redis testing containers
uses: codecov/codecov-action@v5 declare -A redis_version_mapping=(
with: ["8.0-M03"]="8.0-M04-pre"
files: coverage.txt ["7.4.2"]="rs-7.4.0-v2"
token: ${{ secrets.CODECOV_TOKEN }} )
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
test-redis-ce: test-redis-ce:
name: test-redis-ce name: test-redis-ce
@ -47,11 +76,10 @@ jobs:
- "7.4.2" # should use redis stack 7.4 - "7.4.2" # should use redis stack 7.4
- "7.2.7" # should redis stack 7.2 - "7.2.7" # should redis stack 7.2
go-version: go-version:
- "1.22.x"
- "1.23.x" - "1.23.x"
- "1.24.x"
steps: steps:
- name: Checkout code - name: Checkout code
uses: actions/checkout@v4 uses: actions/checkout@v4
@ -61,3 +89,9 @@ jobs:
go-version: ${{matrix.go-version}} go-version: ${{matrix.go-version}}
redis-version: ${{ matrix.redis-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: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
go-version: [ "1.21", "1.22", "1.23" ] go-version: ["1.24"]
steps: steps:
- name: Set up ${{ matrix.go-version }} - name: Set up ${{ matrix.go-version }}
@ -38,4 +38,4 @@ jobs:
- name: Test doc examples - name: Test doc examples
working-directory: ./doctests working-directory: ./doctests
run: go test run: go test -v

View File

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

2
.gitignore vendored
View File

@ -5,4 +5,6 @@ testdata/*
*.tar.gz *.tar.gz
*.dic *.dic
redis8tests.sh redis8tests.sh
coverage.txt
**/coverage.txt
.vscode .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 1. Create your own fork of go-redis
2. Do the changes in your fork 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: 3. If you need a development environment, run `make docker.start`.
```docker run -p 6379:6379 -it redis/redis-stack-server:edge```
4. While developing, make sure the tests pass by running `make tests` > 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 5. If you like the change and think the project could use it, send a
pull request pull request
To see what else is part of the automation, run `invoke -l` To see what else is part of the automation, run `invoke -l`
## Testing ## 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 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 changes against all the go versions supported, as declared by the
[build.yml](./.github/workflows/build.yml) file. [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) GO_MOD_DIRS := $(shell find . -type f -name 'go.mod' -exec dirname {} \; | sort)
export REDIS_MAJOR_VERSION := 7
test: testdeps docker.start:
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 docker compose --profile all up -d --quiet-pull
$(eval GO_VERSION := $(shell go version | cut -d " " -f 3 | cut -d. -f2))
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 \ 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}"; \ echo "go test in $${dir}"; \
(cd "$${dir}" && \ (cd "$${dir}" && \
go mod tidy -compat=1.18 && \ go mod tidy -compat=1.18 && \
go test && \ go vet && \
go test ./... -short -race && \ go test -coverprofile=coverage.txt -covermode=atomic ./... -race); \
go test ./... -run=NONE -bench=. -benchmem && \
env GOOS=linux GOARCH=386 go test && \
go test -coverprofile=coverage.txt -covermode=atomic ./... && \
go vet); \
done done
cd internal/customvet && go build . cd internal/customvet && go build .
go vet -vettool ./internal/customvet/customvet go vet -vettool ./internal/customvet/customvet
docker stop go-redis-redis-stack
testdeps: testdata/redis/src/redis-server bench:
bench: testdeps
go test ./... -test.run=NONE -test.bench=. -test.benchmem go test ./... -test.run=NONE -test.bench=. -test.benchmem
.PHONY: all test testdeps bench fmt .PHONY: all test bench fmt
build: build:
go 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: fmt:
gofumpt -w ./ gofumpt -w ./
goimports -w -local github.com/redis/go-redis ./ 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 > See [OpenTelemetry](https://github.com/redis/go-redis/tree/master/example/otel) example which
> demonstrates how you can use Uptrace to monitor go-redis. > 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? ## How do I Redis?
[Learn for free at Redis University](https://university.redis.com/) [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() { 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()) Expect(client.FlushDB(ctx).Err()).NotTo(HaveOccurred())
val, err := client.FTCreate(ctx, "txt", &redis.FTCreateOptions{}, &redis.FieldSchema{FieldName: "txt", FieldType: redis.SearchFieldTypeText}).Result() val, err := client.FTCreate(ctx, "txt", &redis.FTCreateOptions{}, &redis.FieldSchema{FieldName: "txt", FieldType: redis.SearchFieldTypeText}).Result()
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
@ -322,7 +322,7 @@ var _ = Describe("ACL permissions", Label("NonRedisEnterprise"), func() {
}) })
It("set permissions for module categories", 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()) Expect(client.FlushDB(ctx).Err()).NotTo(HaveOccurred())
val, err := client.FTCreate(ctx, "txt", &redis.FTCreateOptions{}, &redis.FieldSchema{FieldName: "txt", FieldType: redis.SearchFieldTypeText}).Result() val, err := client.FTCreate(ctx, "txt", &redis.FTCreateOptions{}, &redis.FieldSchema{FieldName: "txt", FieldType: redis.SearchFieldTypeText}).Result()
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
@ -419,7 +419,7 @@ var _ = Describe("ACL Categories", func() {
}) })
It("lists acl categories and subcategories with Modules", 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{ aclTestCase := map[string]string{
"search": "FT.CREATE", "search": "FT.CREATE",
"bloom": "bf.add", "bloom": "bf.add",

View File

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

View File

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

View File

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

View File

@ -2,7 +2,7 @@
services: services:
redis: 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 container_name: redis-standalone
environment: environment:
- TLS_ENABLED=yes - TLS_ENABLED=yes
@ -21,9 +21,9 @@ services:
- all-stack - all-stack
- all - all
cluster: osscluster:
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-cluster container_name: redis-osscluster
environment: environment:
- NODES=6 - NODES=6
- PORT=16600 - PORT=16600
@ -31,110 +31,71 @@ services:
ports: ports:
- "16600-16605:16600-16605" - "16600-16605:16600-16605"
volumes: volumes:
- "./dockers/cluster:/redis/work" - "./dockers/osscluster:/redis/work"
profiles: profiles:
- cluster - cluster
- all-stack - all-stack
- all - 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: 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 container_name: redis-sentinel
depends_on: depends_on:
- redis - sentinel-cluster
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
environment: 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 - TLS_ENABLED=yes
- REDIS_CLUSTER=no - REDIS_CLUSTER=no
- PORT=6390 - PORT=6390
command: ${REDIS_EXTRA_ARGS:---enable-debug-command yes --enable-module-command yes --tls-auth-clients optional --save ""} command: ${REDIS_EXTRA_ARGS:---enable-debug-command yes --enable-module-command yes --tls-auth-clients optional --save ""}
ports: ports:
- 6390:6390 - 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 - 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 - 6392:6392
volumes: volumes:
- "./dockers/ring3:/redis/work" - "./dockers/ring:/redis/work"
profiles: profiles:
- ring - ring
- cluster - cluster
- sentinel
- all-stack - 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 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 down-after-milliseconds go-redis-test 5000
sentinel failover-timeout go-redis-test 60000 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 // REMOVE_START
// make sure we are working with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "bikes:models") rdb.Del(ctx, "bikes:models")
// REMOVE_END // REMOVE_END

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -20,7 +20,10 @@ func ExampleClient_geoindex() {
DB: 0, // use default DB DB: 0, // use default DB
Protocol: 2, Protocol: 2,
}) })
// REMOVE_START // REMOVE_START
// make sure we are working with fresh database
rdb.FlushDB(ctx)
rdb.FTDropIndex(ctx, "productidx") rdb.FTDropIndex(ctx, "productidx")
rdb.FTDropIndex(ctx, "geomidx") rdb.FTDropIndex(ctx, "geomidx")
rdb.Del(ctx, "product:46885", "product:46886", "shape:1", "shape:2", "shape:3", "shape:4") 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 // REMOVE_START
// make sure we are working with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "bikes:rentable") rdb.Del(ctx, "bikes:rentable")
// REMOVE_END // REMOVE_END
@ -81,6 +83,8 @@ func ExampleClient_geosearch() {
}) })
// REMOVE_START // REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "bikes:rentable") rdb.Del(ctx, "bikes:rentable")
_, err := rdb.GeoAdd(ctx, "bikes:rentable", _, err := rdb.GeoAdd(ctx, "bikes:rentable",

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -5,6 +5,8 @@ package example_commands_test
import ( import (
"context" "context"
"fmt" "fmt"
"slices"
"strings"
"github.com/redis/go-redis/v9" "github.com/redis/go-redis/v9"
) )
@ -21,6 +23,8 @@ func ExampleClient_query_em() {
// HIDE_END // HIDE_END
// REMOVE_START // REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.FTDropIndex(ctx, "idx:bicycle") rdb.FTDropIndex(ctx, "idx:bicycle")
rdb.FTDropIndex(ctx, "idx:email") rdb.FTDropIndex(ctx, "idx:email")
// REMOVE_END // REMOVE_END
@ -274,11 +278,16 @@ func ExampleClient_query_em() {
fmt.Println(res3.Total) // >>> 5 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) fmt.Println(doc.ID)
} }
// >>> bicycle:5
// >>> bicycle:0 // >>> bicycle:0
// >>> bicycle:5
// >>> bicycle:6 // >>> bicycle:6
// >>> bicycle:7 // >>> bicycle:7
// >>> bicycle:8 // >>> bicycle:8
@ -350,8 +359,8 @@ func ExampleClient_query_em() {
// 1 // 1
// bicycle:0 // bicycle:0
// 5 // 5
// bicycle:5
// bicycle:0 // bicycle:0
// bicycle:5
// bicycle:6 // bicycle:6
// bicycle:7 // bicycle:7
// bicycle:8 // bicycle:8

View File

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

View File

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

View File

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

View File

@ -21,6 +21,8 @@ func ExampleClient_sadd() {
}) })
// REMOVE_START // REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "bikes:racing:france") rdb.Del(ctx, "bikes:racing:france")
rdb.Del(ctx, "bikes:racing:usa") rdb.Del(ctx, "bikes:racing:usa")
// REMOVE_END // REMOVE_END
@ -76,6 +78,8 @@ func ExampleClient_sismember() {
}) })
// REMOVE_START // REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "bikes:racing:france") rdb.Del(ctx, "bikes:racing:france")
rdb.Del(ctx, "bikes:racing:usa") rdb.Del(ctx, "bikes:racing:usa")
// REMOVE_END // REMOVE_END
@ -125,6 +129,8 @@ func ExampleClient_sinter() {
}) })
// REMOVE_START // REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "bikes:racing:france") rdb.Del(ctx, "bikes:racing:france")
rdb.Del(ctx, "bikes:racing:usa") rdb.Del(ctx, "bikes:racing:usa")
// REMOVE_END // REMOVE_END
@ -165,6 +171,8 @@ func ExampleClient_scard() {
}) })
// REMOVE_START // REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "bikes:racing:france") rdb.Del(ctx, "bikes:racing:france")
// REMOVE_END // REMOVE_END
@ -198,6 +206,8 @@ func ExampleClient_saddsmembers() {
}) })
// REMOVE_START // REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "bikes:racing:france") rdb.Del(ctx, "bikes:racing:france")
// REMOVE_END // REMOVE_END
@ -237,6 +247,8 @@ func ExampleClient_smismember() {
}) })
// REMOVE_START // REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "bikes:racing:france") rdb.Del(ctx, "bikes:racing:france")
// REMOVE_END // REMOVE_END
@ -279,6 +291,8 @@ func ExampleClient_sdiff() {
}) })
// REMOVE_START // REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "bikes:racing:france") rdb.Del(ctx, "bikes:racing:france")
rdb.Del(ctx, "bikes:racing:usa") rdb.Del(ctx, "bikes:racing:usa")
// REMOVE_END // REMOVE_END
@ -298,7 +312,6 @@ func ExampleClient_sdiff() {
panic(err) 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) sort.Strings(res13)
@ -319,6 +332,8 @@ func ExampleClient_multisets() {
}) })
// REMOVE_START // REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "bikes:racing:france") rdb.Del(ctx, "bikes:racing:france")
rdb.Del(ctx, "bikes:racing:usa") rdb.Del(ctx, "bikes:racing:usa")
rdb.Del(ctx, "bikes:racing:italy") rdb.Del(ctx, "bikes:racing:italy")
@ -408,6 +423,8 @@ func ExampleClient_srem() {
}) })
// REMOVE_START // REMOVE_START
// start with fresh database
rdb.FlushDB(ctx)
rdb.Del(ctx, "bikes:racing:france") rdb.Del(ctx, "bikes:racing:france")
// REMOVE_END // REMOVE_END

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -4,9 +4,8 @@ import (
"fmt" "fmt"
"net" "net"
"os" "os"
"os/exec"
"path/filepath"
"strconv" "strconv"
"strings"
"sync" "sync"
"testing" "testing"
"time" "time"
@ -28,12 +27,12 @@ const (
const ( const (
sentinelName = "go-redis-test" sentinelName = "go-redis-test"
sentinelMasterPort = "9123" sentinelMasterPort = "9121"
sentinelSlave1Port = "9124" sentinelSlave1Port = "9122"
sentinelSlave2Port = "9125" sentinelSlave2Port = "9123"
sentinelPort1 = "9126" sentinelPort1 = "26379"
sentinelPort2 = "9127" sentinelPort2 = "26380"
sentinelPort3 = "9128" sentinelPort3 = "26381"
) )
var ( var (
@ -49,19 +48,15 @@ var (
var ( var (
sentinelAddrs = []string{":" + sentinelPort1, ":" + sentinelPort2, ":" + sentinelPort3} sentinelAddrs = []string{":" + sentinelPort1, ":" + sentinelPort2, ":" + sentinelPort3}
processes map[string]*redisProcess ringShard1, ringShard2, ringShard3 *redis.Client
sentinelMaster, sentinelSlave1, sentinelSlave2 *redis.Client
redisMain *redisProcess sentinel1, sentinel2, sentinel3 *redis.Client
ringShard1, ringShard2, ringShard3 *redisProcess
sentinelMaster, sentinelSlave1, sentinelSlave2 *redisProcess
sentinel1, sentinel2, sentinel3 *redisProcess
) )
var cluster = &clusterScenario{ var cluster = &clusterScenario{
ports: []string{"16600", "16601", "16602", "16603", "16604", "16605"}, ports: []string{"16600", "16601", "16602", "16603", "16604", "16605"},
nodeIDs: make([]string, 6), nodeIDs: make([]string, 6),
processes: make(map[string]*redisProcess, 6), clients: make(map[string]*redis.Client, 6),
clients: make(map[string]*redis.Client, 6),
} }
// Redis Software Cluster // Redis Software Cluster
@ -70,30 +65,23 @@ var RECluster = false
// Redis Community Edition Docker // Redis Community Edition Docker
var RCEDocker = false 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, // 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. // which have support for label sets, so we can filter tests per redis version.
var RedisMajorVersion = 7 var RedisVersion float64 = 7.2
func SkipBeforeRedisMajor(version int, msg string) { func SkipBeforeRedisVersion(version float64, msg string) {
if RedisMajorVersion < version { if RedisVersion < version {
Skip(fmt.Sprintf("(redis major version < %d) %s", version, msg)) Skip(fmt.Sprintf("(redis version < %f) %s", version, msg))
} }
} }
func SkipAfterRedisMajor(version int, msg string) { func SkipAfterRedisVersion(version float64, msg string) {
if RedisMajorVersion > version { if RedisVersion > version {
Skip(fmt.Sprintf("(redis major version > %d) %s", version, msg)) 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() { var _ = BeforeSuite(func() {
addr := os.Getenv("REDIS_PORT") addr := os.Getenv("REDIS_PORT")
if addr != "" { if addr != "" {
@ -104,35 +92,33 @@ var _ = BeforeSuite(func() {
RECluster, _ = strconv.ParseBool(os.Getenv("RE_CLUSTER")) RECluster, _ = strconv.ParseBool(os.Getenv("RE_CLUSTER"))
RCEDocker, _ = strconv.ParseBool(os.Getenv("RCE_DOCKER")) 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 { if RedisVersion == 0 {
RedisMajorVersion = 7 RedisVersion = 7.2
} }
fmt.Printf("RECluster: %v\n", RECluster) fmt.Printf("RECluster: %v\n", RECluster)
fmt.Printf("RCEDocker: %v\n", RCEDocker) 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 { if RedisVersion < 7.0 || RedisVersion > 9 {
panic("incorrect or not supported redis major version") panic("incorrect or not supported redis version")
} }
if !RECluster && !RCEDocker { redisPort = redisStackPort
redisAddr = redisStackAddr
redisMain, err = startRedis(redisPort) if !RECluster {
ringShard1, err = connectTo(ringShard1Port)
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
ringShard1, err = startRedis(ringShard1Port) ringShard2, err = connectTo(ringShard2Port)
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
ringShard2, err = startRedis(ringShard2Port) ringShard3, err = connectTo(ringShard3Port)
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
ringShard3, err = startRedis(ringShard3Port) sentinelMaster, err = connectTo(sentinelMasterPort)
Expect(err).NotTo(HaveOccurred())
sentinelMaster, err = startRedis(sentinelMasterPort)
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
sentinel1, err = startSentinel(sentinelPort1, sentinelName, sentinelMasterPort) sentinel1, err = startSentinel(sentinelPort1, sentinelName, sentinelMasterPort)
@ -144,24 +130,20 @@ var _ = BeforeSuite(func() {
sentinel3, err = startSentinel(sentinelPort3, sentinelName, sentinelMasterPort) sentinel3, err = startSentinel(sentinelPort3, sentinelName, sentinelMasterPort)
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
sentinelSlave1, err = startRedis( sentinelSlave1, err = connectTo(sentinelSlave1Port)
sentinelSlave1Port, "--slaveof", "127.0.0.1", sentinelMasterPort)
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
sentinelSlave2, err = startRedis( err = sentinelSlave1.SlaveOf(ctx, "127.0.0.1", sentinelMasterPort).Err()
sentinelSlave2Port, "--slaveof", "127.0.0.1", sentinelMasterPort)
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
err = startCluster(ctx, cluster) sentinelSlave2, err = connectTo(sentinelSlave2Port)
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
} else {
redisPort = redisStackPort
redisAddr = redisStackAddr
if !RECluster { err = sentinelSlave2.SlaveOf(ctx, "127.0.0.1", sentinelMasterPort).Err()
// populate cluster node information Expect(err).NotTo(HaveOccurred())
Expect(configureClusterTopology(ctx, cluster)).NotTo(HaveOccurred())
} // populate cluster node information
Expect(configureClusterTopology(ctx, cluster)).NotTo(HaveOccurred())
} }
}) })
@ -169,12 +151,6 @@ var _ = AfterSuite(func() {
if !RECluster { if !RECluster {
Expect(cluster.Close()).NotTo(HaveOccurred()) 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) { func TestGinkgoSuite(t *testing.T) {
@ -204,7 +180,7 @@ func redisOptions() *redis.Options {
} }
return &redis.Options{ return &redis.Options{
Addr: redisAddr, Addr: redisAddr,
DB: 15, DB: 0,
DialTimeout: 10 * time.Second, DialTimeout: 10 * time.Second,
ReadTimeout: 30 * time.Second, ReadTimeout: 30 * time.Second,
@ -256,7 +232,9 @@ func performAsync(n int, cbs ...func(int)) *sync.WaitGroup {
var wg sync.WaitGroup var wg sync.WaitGroup
for _, cb := range cbs { for _, cb := range cbs {
wg.Add(n) 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) { go func(cb func(int), i int) {
defer GinkgoRecover() defer GinkgoRecover()
defer wg.Done() 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) { func connectTo(port string) (*redis.Client, error) {
client := redis.NewClient(&redis.Options{ client := redis.NewClient(&redis.Options{
Addr: ":" + port, Addr: ":" + port,
@ -338,117 +307,22 @@ func connectTo(port string) (*redis.Client, error) {
return client, nil return client, nil
} }
type redisProcess struct { func startSentinel(port, masterName, masterPort string) (*redis.Client, error) {
*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
}
client, err := connectTo(port) client, err := connectTo(port)
if err != nil { if err != nil {
process.Kill()
return nil, err 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{ for _, cmd := range []*redis.StatusCmd{
redis.NewStatusCmd(ctx, "SENTINEL", "MONITOR", masterName, "127.0.0.1", masterPort, "2"), 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) client.Process(ctx, cmd)
if err := cmd.Err(); err != nil { if err := cmd.Err(); err != nil && !strings.Contains(err.Error(), "ERR Duplicate master name.") {
process.Kill()
return nil, fmt.Errorf("%s failed: %w", cmd, err) return nil, fmt.Errorf("%s failed: %w", cmd, err)
} }
} }
p := &redisProcess{process, client} return client, nil
registerProcess(port, p)
return p, nil
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------

View File

@ -6,6 +6,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"net" "net"
"slices"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
@ -19,10 +20,9 @@ import (
) )
type clusterScenario struct { type clusterScenario struct {
ports []string ports []string
nodeIDs []string nodeIDs []string
processes map[string]*redisProcess clients map[string]*redis.Client
clients map[string]*redis.Client
} }
func (s *clusterScenario) slots() []int { 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 return nil
} }
func configureClusterTopology(ctx context.Context, scenario *clusterScenario) error { 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) err := collectNodeInformation(ctx, scenario)
if err != nil { if err != nil {
return err return err
@ -131,7 +128,7 @@ func configureClusterTopology(ctx context.Context, scenario *clusterScenario) er
slots := scenario.slots() slots := scenario.slots()
for pos, master := range scenario.masters() { for pos, master := range scenario.masters() {
err := master.ClusterAddSlotsRange(ctx, slots[pos], slots[pos+1]-1).Err() 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 return err
} }
} }
@ -199,7 +196,7 @@ func configureClusterTopology(ctx context.Context, scenario *clusterScenario) er
return err return err
} }
return assertSlotsEqual(res, wanted) return assertSlotsEqual(res, wanted)
}, 60*time.Second) }, 90*time.Second)
if err != nil { if err != nil {
return err return err
} }
@ -214,31 +211,17 @@ func collectNodeInformation(ctx context.Context, scenario *clusterScenario) erro
Addr: ":" + port, Addr: ":" + port,
}) })
info, err := client.ClusterNodes(ctx).Result() myID, err := client.ClusterMyID(ctx).Result()
if err != nil { if err != nil {
return err return err
} }
scenario.clients[port] = client scenario.clients[port] = client
scenario.nodeIDs[pos] = info[:40] scenario.nodeIDs[pos] = myID
} }
return nil 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 { func assertSlotsEqual(slots, wanted []redis.ClusterSlot) error {
outerLoop: outerLoop:
for _, s2 := range wanted { for _, s2 := range wanted {

View File

@ -89,6 +89,9 @@ var _ = Describe("PubSub", func() {
pubsub := client.Subscribe(ctx, "mychannel", "mychannel2") pubsub := client.Subscribe(ctx, "mychannel", "mychannel2")
defer pubsub.Close() 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() channels, err = client.PubSubChannels(ctx, "mychannel*").Result()
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
Expect(channels).To(ConsistOf([]string{"mychannel", "mychannel2"})) Expect(channels).To(ConsistOf([]string{"mychannel", "mychannel2"}))
@ -135,6 +138,8 @@ var _ = Describe("PubSub", func() {
pubsub := client.Subscribe(ctx, "mychannel", "mychannel2") pubsub := client.Subscribe(ctx, "mychannel", "mychannel2")
defer pubsub.Close() 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() channels, err := client.PubSubNumSub(ctx, "mychannel", "mychannel2", "mychannel3").Result()
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
Expect(channels).To(Equal(map[string]int64{ Expect(channels).To(Equal(map[string]int64{
@ -152,6 +157,8 @@ var _ = Describe("PubSub", func() {
pubsub := client.PSubscribe(ctx, "*") pubsub := client.PSubscribe(ctx, "*")
defer pubsub.Close() 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() num, err = client.PubSubNumPat(ctx).Result()
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
Expect(num).To(Equal(int64(1))) Expect(num).To(Equal(int64(1)))

View File

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

View File

@ -66,11 +66,7 @@ var _ = Describe("Client", func() {
}) })
It("should Stringer", func() { It("should Stringer", func() {
if RECluster { Expect(client.String()).To(Equal(fmt.Sprintf("Redis<:%s db:0>", redisPort)))
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)))
}
}) })
It("supports context", func() { 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")) 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() { It("supports hash tags", func() {
for i := 0; i < 100; i++ { for i := 0; i < 100; i++ {
err := ring.Set(ctx, fmt.Sprintf("key%d{tag}", i), "value", 0).Err() 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 return cmd
} }
// TODO: remove FTProfile
// type FTProfileResult struct { // type FTProfileResult struct {
// Results []interface{} // Results []interface{}
// Profile ProfileDetails // 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 // up until redis 8 the default scorer was TFIDF, in redis 8 it is BM25
// this test expect redis major version >= 8 // this test expect redis major version >= 8
It("should FTSearch WithScores", Label("search", "ftsearch"), func() { 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} text1 := &redis.FieldSchema{FieldName: "description", FieldType: redis.SearchFieldTypeText}
val, err := client.FTCreate(ctx, "idx1", &redis.FTCreateOptions{}, text1).Result() 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 // 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() { 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} text1 := &redis.FieldSchema{FieldName: "description", FieldType: redis.SearchFieldTypeText}
val, err := client.FTCreate(ctx, "idx1", &redis.FTCreateOptions{}, text1).Result() val, err := client.FTCreate(ctx, "idx1", &redis.FTCreateOptions{}, text1).Result()
Expect(err).NotTo(HaveOccurred()) 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() { 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(err).NotTo(HaveOccurred())
Expect(val).To(BeEquivalentTo("OK")) Expect(val).To(BeEquivalentTo("OK"))
res, err := client.FTConfigGet(ctx, "*").Result() res, err := client.FTConfigGet(ctx, "*").Result()
Expect(err).NotTo(HaveOccurred()) 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(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() { 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} title := &redis.FieldSchema{FieldName: "title", FieldType: redis.SearchFieldTypeText, Sortable: false}
description := &redis.FieldSchema{FieldName: "description", 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() 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() { 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{ val, err := client.FTCreate(ctx, "idx1", &redis.FTCreateOptions{
Prefix: []interface{}{"resource:"}, Prefix: []interface{}{"resource:"},
}, &redis.FieldSchema{ }, &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() { 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{}, 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: "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"}}}, &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() { 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{ _, err := client.FTCreate(ctx, "idx1", &redis.FTCreateOptions{}, &redis.FieldSchema{
FieldName: "g", FieldName: "g",
FieldType: redis.SearchFieldTypeGeoShape, 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() { 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:"}}, val, err := client.FTCreate(ctx, "idx1", &redis.FTCreateOptions{Prefix: []interface{}{"property:"}},
&redis.FieldSchema{FieldName: "title", FieldType: redis.SearchFieldTypeText, Sortable: true}, &redis.FieldSchema{FieldName: "title", FieldType: redis.SearchFieldTypeText, Sortable: true},
&redis.FieldSchema{FieldName: "features", FieldType: redis.SearchFieldTypeTag, IndexMissing: 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() { 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:"}}, val, err := client.FTCreate(ctx, "idx1", &redis.FTCreateOptions{Prefix: []interface{}{"property:"}},
&redis.FieldSchema{FieldName: "title", FieldType: redis.SearchFieldTypeText, Sortable: true}, &redis.FieldSchema{FieldName: "title", FieldType: redis.SearchFieldTypeText, Sortable: true},
&redis.FieldSchema{FieldName: "features", FieldType: redis.SearchFieldTypeTag, IndexEmpty: 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()) Expect(clientResp3.Close()).NotTo(HaveOccurred())
}) })
It("should FTConfigSet and FTConfigGet ", Label("search", "ftconfigget", "ftconfigset", "NonRedisEnterprise"), func() { It("should FTConfigSet and FTConfigGet with resp2 and resp3", Label("search", "ftconfigget", "ftconfigset", "NonRedisEnterprise"), func() {
val, err := clientResp3.FTConfigSet(ctx, "TIMEOUT", "100").Result() val, err := clientResp3.FTConfigSet(ctx, "MINPREFIX", "1").Result()
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
Expect(val).To(BeEquivalentTo("OK")) Expect(val).To(BeEquivalentTo("OK"))
res2, err := clientResp2.FTConfigGet(ctx, "TIMEOUT").Result() res2, err := clientResp2.FTConfigGet(ctx, "MINPREFIX").Result()
Expect(err).NotTo(HaveOccurred()) 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(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() { 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/ginkgo/v2"
. "github.com/bsm/gomega" . "github.com/bsm/gomega"
"github.com/redis/go-redis/v9" "github.com/redis/go-redis/v9"
) )
var _ = Describe("Sentinel PROTO 2", func() { var _ = Describe("Sentinel PROTO 2", func() {
var client *redis.Client var client *redis.Client
BeforeEach(func() { BeforeEach(func() {
client = redis.NewFailoverClient(&redis.FailoverOptions{ client = redis.NewFailoverClient(&redis.FailoverOptions{
MasterName: sentinelName, MasterName: sentinelName,
@ -37,7 +35,6 @@ var _ = Describe("Sentinel PROTO 2", func() {
var _ = Describe("Sentinel", func() { var _ = Describe("Sentinel", func() {
var client *redis.Client var client *redis.Client
var master *redis.Client var master *redis.Client
var masterPort string
var sentinel *redis.SentinelClient var sentinel *redis.SentinelClient
BeforeEach(func() { BeforeEach(func() {
@ -61,18 +58,17 @@ var _ = Describe("Sentinel", func() {
Addr: net.JoinHostPort(addr[0], addr[1]), Addr: net.JoinHostPort(addr[0], addr[1]),
MaxRetries: -1, MaxRetries: -1,
}) })
masterPort = addr[1]
// Wait until slaves are picked up by sentinel. // Wait until slaves are picked up by sentinel.
Eventually(func() string { Eventually(func() string {
return sentinel1.Info(ctx).Val() return sentinel1.Info(ctx).Val()
}, "15s", "100ms").Should(ContainSubstring("slaves=2")) }, "20s", "100ms").Should(ContainSubstring("slaves=2"))
Eventually(func() string { Eventually(func() string {
return sentinel2.Info(ctx).Val() return sentinel2.Info(ctx).Val()
}, "15s", "100ms").Should(ContainSubstring("slaves=2")) }, "20s", "100ms").Should(ContainSubstring("slaves=2"))
Eventually(func() string { Eventually(func() string {
return sentinel3.Info(ctx).Val() return sentinel3.Info(ctx).Val()
}, "15s", "100ms").Should(ContainSubstring("slaves=2")) }, "20s", "100ms").Should(ContainSubstring("slaves=2"))
}) })
AfterEach(func() { AfterEach(func() {
@ -96,7 +92,7 @@ var _ = Describe("Sentinel", func() {
Eventually(func() []string { Eventually(func() []string {
slavesAddr = redis.GetSlavesAddrByName(ctx, sentinel, sentinelName) slavesAddr = redis.GetSlavesAddrByName(ctx, sentinel, sentinelName)
return slavesAddr return slavesAddr
}, "15s", "100ms").Should(HaveLen(2)) }, "20s", "50ms").Should(HaveLen(2))
Eventually(func() bool { Eventually(func() bool {
sync := true sync := true
for _, addr := range slavesAddr { for _, addr := range slavesAddr {
@ -108,36 +104,35 @@ var _ = Describe("Sentinel", func() {
_ = slave.Close() _ = slave.Close()
} }
return sync return sync
}, "15s", "100ms").Should(BeTrue()) }, "20s", "50ms").Should(BeTrue())
// Create subscription. // Create subscription.
pub := client.Subscribe(ctx, "foo") pub := client.Subscribe(ctx, "foo")
ch := pub.Channel() ch := pub.Channel()
// Kill master. // Kill master.
err = master.Shutdown(ctx).Err() /*
Expect(err).NotTo(HaveOccurred()) err = master.Shutdown(ctx).Err()
Eventually(func() error { Expect(err).NotTo(HaveOccurred())
return master.Ping(ctx).Err() Eventually(func() error {
}, "15s", "100ms").Should(HaveOccurred()) return master.Ping(ctx).Err()
}, "20s", "50ms").Should(HaveOccurred())
*/
// Check that client picked up new master. // Check that client picked up new master.
Eventually(func() string { Eventually(func() string {
return client.Get(ctx, "foo").Val() return client.Get(ctx, "foo").Val()
}, "15s", "100ms").Should(Equal("master")) }, "20s", "100ms").Should(Equal("master"))
// Check if subscription is renewed. // Check if subscription is renewed.
var msg *redis.Message var msg *redis.Message
Eventually(func() <-chan *redis.Message { Eventually(func() <-chan *redis.Message {
_ = client.Publish(ctx, "foo", "hello").Err() _ = client.Publish(ctx, "foo", "hello").Err()
return ch return ch
}, "15s", "100ms").Should(Receive(&msg)) }, "20s", "100ms").Should(Receive(&msg))
Expect(msg.Channel).To(Equal("foo")) Expect(msg.Channel).To(Equal("foo"))
Expect(msg.Payload).To(Equal("hello")) Expect(msg.Payload).To(Equal("hello"))
Expect(pub.Close()).NotTo(HaveOccurred()) Expect(pub.Close()).NotTo(HaveOccurred())
_, err = startRedis(masterPort)
Expect(err).NotTo(HaveOccurred())
}) })
It("supports DB selection", func() { It("supports DB selection", func() {
@ -197,7 +192,6 @@ var _ = Describe("NewFailoverClusterClient PROTO 2", func() {
var _ = Describe("NewFailoverClusterClient", func() { var _ = Describe("NewFailoverClusterClient", func() {
var client *redis.ClusterClient var client *redis.ClusterClient
var master *redis.Client var master *redis.Client
var masterPort string
BeforeEach(func() { BeforeEach(func() {
client = redis.NewFailoverClusterClient(&redis.FailoverOptions{ client = redis.NewFailoverClusterClient(&redis.FailoverOptions{
@ -221,18 +215,17 @@ var _ = Describe("NewFailoverClusterClient", func() {
Addr: net.JoinHostPort(addr[0], addr[1]), Addr: net.JoinHostPort(addr[0], addr[1]),
MaxRetries: -1, MaxRetries: -1,
}) })
masterPort = addr[1]
// Wait until slaves are picked up by sentinel. // Wait until slaves are picked up by sentinel.
Eventually(func() string { Eventually(func() string {
return sentinel1.Info(ctx).Val() return sentinel1.Info(ctx).Val()
}, "15s", "100ms").Should(ContainSubstring("slaves=2")) }, "20s", "100ms").Should(ContainSubstring("slaves=2"))
Eventually(func() string { Eventually(func() string {
return sentinel2.Info(ctx).Val() return sentinel2.Info(ctx).Val()
}, "15s", "100ms").Should(ContainSubstring("slaves=2")) }, "20s", "100ms").Should(ContainSubstring("slaves=2"))
Eventually(func() string { Eventually(func() string {
return sentinel3.Info(ctx).Val() return sentinel3.Info(ctx).Val()
}, "15s", "100ms").Should(ContainSubstring("slaves=2")) }, "20s", "100ms").Should(ContainSubstring("slaves=2"))
}) })
AfterEach(func() { AfterEach(func() {
@ -241,7 +234,6 @@ var _ = Describe("NewFailoverClusterClient", func() {
}) })
It("should facilitate failover", func() { It("should facilitate failover", func() {
Skip("Flaky Test")
// Set value. // Set value.
err := client.Set(ctx, "foo", "master", 0).Err() err := client.Set(ctx, "foo", "master", 0).Err()
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
@ -250,7 +242,7 @@ var _ = Describe("NewFailoverClusterClient", func() {
// Verify. // Verify.
Eventually(func() string { Eventually(func() string {
return client.Get(ctx, "foo").Val() return client.Get(ctx, "foo").Val()
}, "15s", "1ms").Should(Equal("master")) }, "20s", "1ms").Should(Equal("master"))
} }
// Create subscription. // Create subscription.
@ -258,33 +250,32 @@ var _ = Describe("NewFailoverClusterClient", func() {
ch := sub.Channel() ch := sub.Channel()
// Kill master. // Kill master.
err = master.Shutdown(ctx).Err() /*
Expect(err).NotTo(HaveOccurred()) err = master.Shutdown(ctx).Err()
Eventually(func() error { Expect(err).NotTo(HaveOccurred())
return master.Ping(ctx).Err() Eventually(func() error {
}, "15s", "100ms").Should(HaveOccurred()) return master.Ping(ctx).Err()
}, "20s", "100ms").Should(HaveOccurred())
*/
// Check that client picked up new master. // Check that client picked up new master.
Eventually(func() string { Eventually(func() string {
return client.Get(ctx, "foo").Val() return client.Get(ctx, "foo").Val()
}, "15s", "100ms").Should(Equal("master")) }, "20s", "100ms").Should(Equal("master"))
// Check if subscription is renewed. // Check if subscription is renewed.
var msg *redis.Message var msg *redis.Message
Eventually(func() <-chan *redis.Message { Eventually(func() <-chan *redis.Message {
_ = client.Publish(ctx, "foo", "hello").Err() _ = client.Publish(ctx, "foo", "hello").Err()
return ch return ch
}, "15s", "100ms").Should(Receive(&msg)) }, "20s", "100ms").Should(Receive(&msg))
Expect(msg.Channel).To(Equal("foo")) Expect(msg.Channel).To(Equal("foo"))
Expect(msg.Payload).To(Equal("hello")) Expect(msg.Payload).To(Equal("hello"))
Expect(sub.Close()).NotTo(HaveOccurred()) Expect(sub.Close()).NotTo(HaveOccurred())
_, err = startRedis(masterPort)
Expect(err).NotTo(HaveOccurred())
}) })
It("should sentinel cluster client setname", func() { It("should sentinel cluster client setname", func() {
Skip("Flaky Test")
err := client.ForEachShard(ctx, func(ctx context.Context, c *redis.Client) error { err := client.ForEachShard(ctx, func(ctx context.Context, c *redis.Client) error {
return c.Ping(ctx).Err() return c.Ping(ctx).Err()
}) })
@ -299,7 +290,6 @@ var _ = Describe("NewFailoverClusterClient", func() {
}) })
It("should sentinel cluster PROTO 3", func() { It("should sentinel cluster PROTO 3", func() {
Skip("Flaky Test")
_ = client.ForEachShard(ctx, func(ctx context.Context, c *redis.Client) error { _ = client.ForEachShard(ctx, func(ctx context.Context, c *redis.Client) error {
val, err := client.Do(ctx, "HELLO").Result() val, err := client.Do(ctx, "HELLO").Result()
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
@ -317,8 +307,8 @@ var _ = Describe("SentinelAclAuth", func() {
var client *redis.Client var client *redis.Client
var sentinel *redis.SentinelClient var sentinel *redis.SentinelClient
sentinels := func() []*redisProcess { sentinels := func() []*redis.Client {
return []*redisProcess{sentinel1, sentinel2, sentinel3} return []*redis.Client{sentinel1, sentinel2, sentinel3}
} }
BeforeEach(func() { BeforeEach(func() {
@ -328,7 +318,7 @@ var _ = Describe("SentinelAclAuth", func() {
"+sentinel|myid", "+sentinel|replicas", "+sentinel|sentinels") "+sentinel|myid", "+sentinel|replicas", "+sentinel|sentinels")
for _, process := range sentinels() { for _, process := range sentinels() {
err := process.Client.Process(ctx, authCmd) err := process.Process(ctx, authCmd)
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
} }
@ -356,7 +346,7 @@ var _ = Describe("SentinelAclAuth", func() {
for _, process := range sentinels() { for _, process := range sentinels() {
Eventually(func() string { Eventually(func() string {
return process.Info(ctx).Val() 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) unauthCommand := redis.NewStatusCmd(ctx, "ACL", "DELUSER", aclSentinelUsername)
for _, process := range sentinels() { for _, process := range sentinels() {
err := process.Client.Process(ctx, unauthCommand) err := process.Process(ctx, unauthCommand)
Expect(err).NotTo(HaveOccurred()) 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() { 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() result, err := client.TSCreate(ctx, "1").Result()
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
Expect(result).To(BeEquivalentTo("OK")) Expect(result).To(BeEquivalentTo("OK"))
@ -139,6 +140,7 @@ var _ = Describe("RedisTimeseries commands", Label("timeseries"), func() {
{Timestamp: 1013, Value: 10.0}})) {Timestamp: 1013, Value: 10.0}}))
}) })
It("should TSAdd and TSAddWithArgs", Label("timeseries", "tsadd", "tsaddWithArgs", "NonRedisEnterprise"), func() { 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() result, err := client.TSAdd(ctx, "1", 1, 1).Result()
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
Expect(result).To(BeEquivalentTo(1)) Expect(result).To(BeEquivalentTo(1))
@ -232,6 +234,7 @@ var _ = Describe("RedisTimeseries commands", Label("timeseries"), func() {
}) })
It("should TSAlter", Label("timeseries", "tsalter", "NonRedisEnterprise"), 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() result, err := client.TSCreate(ctx, "1").Result()
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
Expect(result).To(BeEquivalentTo("OK")) 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() { 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++ { for i := 0; i < 100; i++ {
_, err := client.TSIncrBy(ctx, "1", 1).Result() _, err := client.TSIncrBy(ctx, "1", 1).Result()
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())

View File

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