Skip to content

Commit 56829d4

Browse files
authored
Merge branch 'master' into implement-tls-url-parameters-pr2076
2 parents 7add47d + 10121e9 commit 56829d4

File tree

14 files changed

+327
-48
lines changed

14 files changed

+327
-48
lines changed

CONTRIBUTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ Here's how to get started with your code contribution:
3737
> Note: this clones and builds the docker containers specified in `docker-compose.yml`, to understand more about
3838
> the infrastructure that will be started you can check the `docker-compose.yml`. You also have the possiblity
3939
> to specify the redis image that will be pulled with the env variable `CLIENT_LIBS_TEST_IMAGE`.
40-
> By default the docker image that will be pulled and started is `redislabs/client-libs-test:rs-7.4.0-v2`.
40+
> By default the docker image that will be pulled and started is `redislabs/client-libs-test:8.2.1-pre`.
4141
> If you want to test with newer Redis version, using a newer version of `redislabs/client-libs-test` should work out of the box.
4242
4343
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).

Makefile

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
GO_MOD_DIRS := $(shell find . -type f -name 'go.mod' -exec dirname {} \; | sort)
2+
REDIS_VERSION ?= 8.2
3+
RE_CLUSTER ?= false
4+
RCE_DOCKER ?= true
5+
CLIENT_LIBS_TEST_IMAGE ?= redislabs/client-libs-test:8.2.1-pre
26

37
docker.start:
8+
export RE_CLUSTER=$(RE_CLUSTER) && \
9+
export RCE_DOCKER=$(RCE_DOCKER) && \
10+
export REDIS_VERSION=$(REDIS_VERSION) && \
11+
export CLIENT_LIBS_TEST_IMAGE=$(CLIENT_LIBS_TEST_IMAGE) && \
412
docker compose --profile all up -d --quiet-pull
513

614
docker.stop:
@@ -27,6 +35,9 @@ test.ci:
2735
set -e; for dir in $(GO_MOD_DIRS); do \
2836
echo "go test in $${dir}"; \
2937
(cd "$${dir}" && \
38+
export RE_CLUSTER=$(RE_CLUSTER) && \
39+
export RCE_DOCKER=$(RCE_DOCKER) && \
40+
export REDIS_VERSION=$(REDIS_VERSION) && \
3041
go mod tidy -compat=1.18 && \
3142
go vet && \
3243
go test -v -coverprofile=coverage.txt -covermode=atomic ./... -race -skip Example); \
@@ -38,6 +49,9 @@ test.ci.skip-vectorsets:
3849
set -e; for dir in $(GO_MOD_DIRS); do \
3950
echo "go test in $${dir} (skipping vector sets)"; \
4051
(cd "$${dir}" && \
52+
export RE_CLUSTER=$(RE_CLUSTER) && \
53+
export RCE_DOCKER=$(RCE_DOCKER) && \
54+
export REDIS_VERSION=$(REDIS_VERSION) && \
4155
go mod tidy -compat=1.18 && \
4256
go vet && \
4357
go test -v -coverprofile=coverage.txt -covermode=atomic ./... -race \
@@ -47,11 +61,17 @@ test.ci.skip-vectorsets:
4761
go vet -vettool ./internal/customvet/customvet
4862

4963
bench:
64+
export RE_CLUSTER=$(RE_CLUSTER) && \
65+
export RCE_DOCKER=$(RCE_DOCKER) && \
66+
export REDIS_VERSION=$(REDIS_VERSION) && \
5067
go test ./... -test.run=NONE -test.bench=. -test.benchmem -skip Example
5168

5269
.PHONY: all test test.ci test.ci.skip-vectorsets bench fmt
5370

5471
build:
72+
export RE_CLUSTER=$(RE_CLUSTER) && \
73+
export RCE_DOCKER=$(RCE_DOCKER) && \
74+
export REDIS_VERSION=$(REDIS_VERSION) && \
5575
go build .
5676

5777
fmt:

README.md

Lines changed: 2 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -432,36 +432,9 @@ res, err := rdb.Do(ctx, "set", "key", "value").Result()
432432
433433
## Run the test
434434
435-
go-redis will start a redis-server and run the test cases.
436-
437-
The paths of redis-server bin file and redis config file are defined in `main_test.go`:
438-
439-
```go
440-
var (
441-
redisServerBin, _ = filepath.Abs(filepath.Join("testdata", "redis", "src", "redis-server"))
442-
redisServerConf, _ = filepath.Abs(filepath.Join("testdata", "redis", "redis.conf"))
443-
)
444-
```
445-
446-
For local testing, you can change the variables to refer to your local files, or create a soft link
447-
to the corresponding folder for redis-server and copy the config file to `testdata/redis/`:
448-
449-
```shell
450-
ln -s /usr/bin/redis-server ./go-redis/testdata/redis/src
451-
cp ./go-redis/testdata/redis.conf ./go-redis/testdata/redis/
452-
```
453-
454-
Lastly, run:
455-
456-
```shell
457-
go test
458-
```
459-
460-
Another option is to run your specific tests with an already running redis. The example below, tests
461-
against a redis running on port 9999.:
462-
435+
Recommended to use Docker, just need to run:
463436
```shell
464-
REDIS_PORT=9999 go test <your options>
437+
make test
465438
```
466439
467440
## See also

commands.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,7 @@ var (
253253
_ Cmdable = (*Tx)(nil)
254254
_ Cmdable = (*Ring)(nil)
255255
_ Cmdable = (*ClusterClient)(nil)
256+
_ Cmdable = (*Pipeline)(nil)
256257
)
257258

258259
type cmdable func(ctx context.Context, cmd Cmder) error

docker-compose.yml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
services:
44
redis:
5-
image: ${CLIENT_LIBS_TEST_IMAGE:-redislabs/client-libs-test:rs-7.4.0-v2}
5+
image: ${CLIENT_LIBS_TEST_IMAGE:-redislabs/client-libs-test:8.2.1-pre}
66
platform: linux/amd64
77
container_name: redis-standalone
88
environment:
@@ -23,7 +23,7 @@ services:
2323
- all
2424

2525
osscluster:
26-
image: ${CLIENT_LIBS_TEST_IMAGE:-redislabs/client-libs-test:rs-7.4.0-v2}
26+
image: ${CLIENT_LIBS_TEST_IMAGE:-redislabs/client-libs-test:8.2.1-pre}
2727
platform: linux/amd64
2828
container_name: redis-osscluster
2929
environment:
@@ -40,7 +40,7 @@ services:
4040
- all
4141

4242
sentinel-cluster:
43-
image: ${CLIENT_LIBS_TEST_IMAGE:-redislabs/client-libs-test:rs-7.4.0-v2}
43+
image: ${CLIENT_LIBS_TEST_IMAGE:-redislabs/client-libs-test:8.2.1-pre}
4444
platform: linux/amd64
4545
container_name: redis-sentinel-cluster
4646
network_mode: "host"
@@ -60,7 +60,7 @@ services:
6060
- all
6161

6262
sentinel:
63-
image: ${CLIENT_LIBS_TEST_IMAGE:-redislabs/client-libs-test:rs-7.4.0-v2}
63+
image: ${CLIENT_LIBS_TEST_IMAGE:-redislabs/client-libs-test:8.2.1-pre}
6464
platform: linux/amd64
6565
container_name: redis-sentinel
6666
depends_on:
@@ -84,7 +84,7 @@ services:
8484
- all
8585

8686
ring-cluster:
87-
image: ${CLIENT_LIBS_TEST_IMAGE:-redislabs/client-libs-test:rs-7.4.0-v2}
87+
image: ${CLIENT_LIBS_TEST_IMAGE:-redislabs/client-libs-test:8.2.1-pre}
8888
platform: linux/amd64
8989
container_name: redis-ring-cluster
9090
environment:

extra/redisotel/metrics.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,23 @@ func reportPoolStats(rdb *redis.Client, conf *config) (metric.Registration, erro
155155
return nil, err
156156
}
157157

158+
waits, err := conf.meter.Int64ObservableUpDownCounter(
159+
"db.client.connections.waits",
160+
metric.WithDescription("The number of times a connection was waited for"),
161+
)
162+
if err != nil {
163+
return nil, err
164+
}
165+
166+
waitsDuration, err := conf.meter.Int64ObservableUpDownCounter(
167+
"db.client.connections.waits_duration",
168+
metric.WithDescription("The total time spent for waiting a connection in nanoseconds"),
169+
metric.WithUnit("ns"),
170+
)
171+
if err != nil {
172+
return nil, err
173+
}
174+
158175
timeouts, err := conf.meter.Int64ObservableUpDownCounter(
159176
"db.client.connections.timeouts",
160177
metric.WithDescription("The number of connection timeouts that have occurred trying to obtain a connection from the pool"),
@@ -191,6 +208,9 @@ func reportPoolStats(rdb *redis.Client, conf *config) (metric.Registration, erro
191208
o.ObserveInt64(usage, int64(stats.IdleConns), metric.WithAttributeSet(idleAttrs))
192209
o.ObserveInt64(usage, int64(stats.TotalConns-stats.IdleConns), metric.WithAttributeSet(usedAttrs))
193210

211+
o.ObserveInt64(waits, int64(stats.WaitCount), metric.WithAttributeSet(poolAttrs))
212+
o.ObserveInt64(waitsDuration, stats.WaitDurationNs, metric.WithAttributeSet(poolAttrs))
213+
194214
o.ObserveInt64(timeouts, int64(stats.Timeouts), metric.WithAttributeSet(poolAttrs))
195215
o.ObserveInt64(hits, int64(stats.Hits), metric.WithAttributeSet(poolAttrs))
196216
o.ObserveInt64(misses, int64(stats.Misses), metric.WithAttributeSet(poolAttrs))

internal_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,3 +383,38 @@ var _ = Describe("ClusterClient", func() {
383383
})
384384
})
385385
})
386+
387+
var _ = Describe("isLoopback", func() {
388+
DescribeTable("should correctly identify loopback addresses",
389+
func(host string, expected bool) {
390+
result := isLoopback(host)
391+
Expect(result).To(Equal(expected))
392+
},
393+
// IP addresses
394+
Entry("IPv4 loopback", "127.0.0.1", true),
395+
Entry("IPv6 loopback", "::1", true),
396+
Entry("IPv4 non-loopback", "192.168.1.1", false),
397+
Entry("IPv6 non-loopback", "2001:db8::1", false),
398+
399+
// Well-known loopback hostnames
400+
Entry("localhost lowercase", "localhost", true),
401+
Entry("localhost uppercase", "LOCALHOST", true),
402+
Entry("localhost mixed case", "LocalHost", true),
403+
404+
// Docker-specific loopbacks
405+
Entry("host.docker.internal", "host.docker.internal", true),
406+
Entry("HOST.DOCKER.INTERNAL", "HOST.DOCKER.INTERNAL", true),
407+
Entry("custom.docker.internal", "custom.docker.internal", true),
408+
Entry("app.docker.internal", "app.docker.internal", true),
409+
410+
// Non-loopback hostnames
411+
Entry("redis hostname", "redis-cluster", false),
412+
Entry("FQDN", "redis.example.com", false),
413+
Entry("docker but not internal", "redis.docker.com", false),
414+
415+
// Edge cases
416+
Entry("empty string", "", false),
417+
Entry("invalid IP", "256.256.256.256", false),
418+
Entry("partial docker internal", "docker.internal", false),
419+
)
420+
})

main_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ var RCEDocker = false
6868
// Notes version of redis we are executing tests against.
6969
// This can be used before we change the bsm fork of ginkgo for one,
7070
// which have support for label sets, so we can filter tests per redis version.
71-
var RedisVersion float64 = 7.2
71+
var RedisVersion float64 = 8.2
7272

7373
func SkipBeforeRedisVersion(version float64, msg string) {
7474
if RedisVersion < version {
@@ -95,7 +95,7 @@ var _ = BeforeSuite(func() {
9595
RedisVersion, _ = strconv.ParseFloat(strings.Trim(os.Getenv("REDIS_VERSION"), "\""), 64)
9696

9797
if RedisVersion == 0 {
98-
RedisVersion = 7.2
98+
RedisVersion = 8.2
9999
}
100100

101101
fmt.Printf("RECluster: %v\n", RECluster)

osscluster.go

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -847,12 +847,25 @@ func replaceLoopbackHost(nodeAddr, originHost string) string {
847847
return net.JoinHostPort(originHost, nodePort)
848848
}
849849

850+
// isLoopback returns true if the host is a loopback address.
851+
// For IP addresses, it uses net.IP.IsLoopback().
852+
// For hostnames, it recognizes well-known loopback hostnames like "localhost"
853+
// and Docker-specific loopback patterns like "*.docker.internal".
850854
func isLoopback(host string) bool {
851855
ip := net.ParseIP(host)
852-
if ip == nil {
856+
if ip != nil {
857+
return ip.IsLoopback()
858+
}
859+
860+
if strings.ToLower(host) == "localhost" {
853861
return true
854862
}
855-
return ip.IsLoopback()
863+
864+
if strings.HasSuffix(strings.ToLower(host), ".docker.internal") {
865+
return true
866+
}
867+
868+
return false
856869
}
857870

858871
func (c *clusterState) slotMasterNode(slot int) (*clusterNode, error) {
@@ -1887,14 +1900,33 @@ func (c *ClusterClient) pubSub() *PubSub {
18871900
}
18881901

18891902
var err error
1903+
18901904
if len(channels) > 0 {
18911905
slot := hashtag.Slot(channels[0])
1892-
node, err = c.slotMasterNode(ctx, slot)
1906+
1907+
// newConn in PubSub is only used for subscription connections, so it is safe to
1908+
// assume that a slave node can always be used when client options specify ReadOnly.
1909+
if c.opt.ReadOnly {
1910+
state, err := c.state.Get(ctx)
1911+
if err != nil {
1912+
return nil, err
1913+
}
1914+
1915+
node, err = c.slotReadOnlyNode(state, slot)
1916+
if err != nil {
1917+
return nil, err
1918+
}
1919+
} else {
1920+
node, err = c.slotMasterNode(ctx, slot)
1921+
if err != nil {
1922+
return nil, err
1923+
}
1924+
}
18931925
} else {
18941926
node, err = c.nodes.Random()
1895-
}
1896-
if err != nil {
1897-
return nil, err
1927+
if err != nil {
1928+
return nil, err
1929+
}
18981930
}
18991931

19001932
cn, err := node.Client.newConn(context.TODO())

0 commit comments

Comments
 (0)