Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 89bfe30

Browse files
authoredMar 27, 2025··
Merge branch 'redis:master' into master
2 parents f2460b3 + cb3aa19 commit 89bfe30

38 files changed

+619
-346
lines changed
 

Diff for: ‎.github/wordlist.txt

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ Lua
2929
MSSQL
3030
namespace
3131
NoSQL
32+
OpenTelemetry
3233
ORM
3334
Packagist
3435
PhpRedis

Diff for: ‎.github/workflows/golangci-lint.yml

+4-1
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,7 @@ jobs:
2121
steps:
2222
- uses: actions/checkout@v4
2323
- name: golangci-lint
24-
uses: golangci/golangci-lint-action@v6.5.1
24+
uses: golangci/golangci-lint-action@v6.5.2
25+
with:
26+
verify: false # disable verifying the configuration since golangci is currently introducing breaking changes in the configuration
27+

Diff for: ‎.github/workflows/test-redis-enterprise.yml

-1
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,4 @@ jobs:
5454
--ginkgo.skip-file="sentinel_test.go" \
5555
--ginkgo.skip-file="osscluster_test.go" \
5656
--ginkgo.skip-file="pubsub_test.go" \
57-
--ginkgo.skip-file="gears_commands_test.go" \
5857
--ginkgo.label-filter='!NonRedisEnterprise'

Diff for: ‎README.md

+42-5
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,24 @@ func ExampleClient() *redis.Client {
167167

168168
```
169169

170+
### Instrument with OpenTelemetry
171+
172+
```go
173+
import (
174+
"github.com/redis/go-redis/v9"
175+
"github.com/redis/go-redis/extra/redisotel/v9"
176+
"errors"
177+
)
178+
179+
func main() {
180+
...
181+
rdb := redis.NewClient(&redis.Options{...})
182+
183+
if err := errors.Join(redisotel.InstrumentTracing(rdb), redisotel.InstrumentMetrics(rdb)); err != nil {
184+
log.Fatal(err)
185+
}
186+
```
187+
170188
171189
### Advanced Configuration
172190
@@ -178,16 +196,18 @@ By default, go-redis automatically sends the client library name and version dur
178196
179197
#### Disabling Identity Verification
180198
181-
When connection identity verification is not required or needs to be explicitly disabled, a `DisableIndentity` configuration option exists. In V10 of this library, `DisableIndentity` will become `DisableIdentity` in order to fix the associated typo.
199+
When connection identity verification is not required or needs to be explicitly disabled, a `DisableIdentity` configuration option exists.
200+
Initially there was a typo and the option was named `DisableIndentity` instead of `DisableIdentity`. The misspelled option is marked as Deprecated and will be removed in V10 of this library.
201+
Although both options will work at the moment, the correct option is `DisableIdentity`. The deprecated option will be removed in V10 of this library, so please use the correct option name to avoid any issues.
182202
183-
To disable verification, set the `DisableIndentity` option to `true` in the Redis client options:
203+
To disable verification, set the `DisableIdentity` option to `true` in the Redis client options:
184204
185205
```go
186206
rdb := redis.NewClient(&redis.Options{
187207
Addr: "localhost:6379",
188208
Password: "",
189209
DB: 0,
190-
DisableIndentity: true, // Disable set-info on connect
210+
DisableIdentity: true, // Disable set-info on connect
191211
})
192212
```
193213
@@ -213,9 +233,26 @@ val1 := client.FTSearchWithArgs(ctx, "txt", "foo bar", &redis.FTSearchOptions{})
213233
214234
In the Redis-Search module, **the default dialect is 2**. If needed, you can explicitly specify a different dialect using the appropriate configuration in your queries.
215235
216-
## Contributing
236+
**Important**: Be aware that the query dialect may impact the results returned. If needed, you can revert to a different dialect version by passing the desired dialect in the arguments of the command you want to execute.
237+
For example:
238+
```
239+
res2, err := rdb.FTSearchWithArgs(ctx,
240+
"idx:bicycle",
241+
"@pickup_zone:[CONTAINS $bike]",
242+
&redis.FTSearchOptions{
243+
Params: map[string]interface{}{
244+
"bike": "POINT(-0.1278 51.5074)",
245+
},
246+
DialectVersion: 3,
247+
},
248+
).Result()
249+
```
250+
You can find further details in the [query dialect documentation](https://redis.io/docs/latest/develop/interact/search-and-query/advanced-concepts/dialects/).
217251
218-
Please see [out contributing guidelines](CONTRIBUTING.md) to help us improve this library!
252+
## Contributing
253+
We welcome contributions to the go-redis library! If you have a bug fix, feature request, or improvement, please open an issue or pull request on GitHub.
254+
We appreciate your help in making go-redis better for everyone.
255+
If you are interested in contributing to the go-redis library, please check out our [contributing guidelines](CONTRIBUTING.md) for more information on how to get started.
219256
220257
## Look and feel
221258

Diff for: ‎bench_decode_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ func NewClientStub(resp []byte) *ClientStub {
3030
Dialer: func(ctx context.Context, network, addr string) (net.Conn, error) {
3131
return stub.stubConn(initHello), nil
3232
},
33-
DisableIndentity: true,
33+
DisableIdentity: true,
3434
})
3535
return stub
3636
}
@@ -46,7 +46,7 @@ func NewClusterClientStub(resp []byte) *ClientStub {
4646
Dialer: func(ctx context.Context, network, addr string) (net.Conn, error) {
4747
return stub.stubConn(initHello), nil
4848
},
49-
DisableIndentity: true,
49+
DisableIdentity: true,
5050

5151
ClusterSlots: func(_ context.Context) ([]ClusterSlot, error) {
5252
return []ClusterSlot{

Diff for: ‎commands.go

+6-1
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,6 @@ type Cmdable interface {
213213
ACLCmdable
214214
BitMapCmdable
215215
ClusterCmdable
216-
GearsCmdable
217216
GenericCmdable
218217
GeoCmdable
219218
HashCmdable
@@ -425,6 +424,12 @@ func (c cmdable) Ping(ctx context.Context) *StatusCmd {
425424
return cmd
426425
}
427426

427+
func (c cmdable) Do(ctx context.Context, args ...interface{}) *Cmd {
428+
cmd := NewCmd(ctx, args...)
429+
_ = c(ctx, cmd)
430+
return cmd
431+
}
432+
428433
func (c cmdable) Quit(_ context.Context) *StatusCmd {
429434
panic("not implemented")
430435
}

Diff for: ‎commands_test.go

+148-1
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,12 @@ var _ = Describe("Commands", func() {
8484
Expect(ping.Val()).To(Equal("PONG"))
8585
})
8686

87+
It("should Ping with Do method", func() {
88+
result := client.Conn().Do(ctx, "PING")
89+
Expect(result.Err()).NotTo(HaveOccurred())
90+
Expect(result.Val()).To(Equal("PONG"))
91+
})
92+
8793
It("should Wait", func() {
8894
const wait = 3 * time.Second
8995

@@ -2672,7 +2678,6 @@ var _ = Describe("Commands", func() {
26722678
Expect(res).To(Equal([]int64{1, 1, -2}))
26732679
})
26742680

2675-
26762681
It("should HPExpire", Label("hash-expiration", "NonRedisEnterprise"), func() {
26772682
SkipBeforeRedisVersion(7.4, "doesn't work with older redis stack images")
26782683
res, err := client.HPExpire(ctx, "no_such_key", 10*time.Second, "field1", "field2", "field3").Result()
@@ -2825,6 +2830,148 @@ var _ = Describe("Commands", func() {
28252830
Expect(err).NotTo(HaveOccurred())
28262831
Expect(res[0]).To(BeNumerically("~", 10*time.Second.Milliseconds(), 1))
28272832
})
2833+
2834+
It("should HGETDEL", Label("hash", "HGETDEL"), func() {
2835+
SkipBeforeRedisVersion(7.9, "requires Redis 8.x")
2836+
2837+
err := client.HSet(ctx, "myhash", "f1", "val1", "f2", "val2", "f3", "val3").Err()
2838+
Expect(err).NotTo(HaveOccurred())
2839+
2840+
// Execute HGETDEL on fields f1 and f2.
2841+
res, err := client.HGetDel(ctx, "myhash", "f1", "f2").Result()
2842+
Expect(err).NotTo(HaveOccurred())
2843+
// Expect the returned values for f1 and f2.
2844+
Expect(res).To(Equal([]string{"val1", "val2"}))
2845+
2846+
// Verify that f1 and f2 have been deleted, while f3 remains.
2847+
remaining, err := client.HMGet(ctx, "myhash", "f1", "f2", "f3").Result()
2848+
Expect(err).NotTo(HaveOccurred())
2849+
Expect(remaining[0]).To(BeNil())
2850+
Expect(remaining[1]).To(BeNil())
2851+
Expect(remaining[2]).To(Equal("val3"))
2852+
})
2853+
2854+
It("should return nil responses for HGETDEL on non-existent key", Label("hash", "HGETDEL"), func() {
2855+
SkipBeforeRedisVersion(7.9, "requires Redis 8.x")
2856+
// HGETDEL on a key that does not exist.
2857+
res, err := client.HGetDel(ctx, "nonexistent", "f1", "f2").Result()
2858+
Expect(err).To(BeNil())
2859+
Expect(res).To(Equal([]string{"", ""}))
2860+
})
2861+
2862+
// -----------------------------
2863+
// HGETEX with various TTL options
2864+
// -----------------------------
2865+
It("should HGETEX with EX option", Label("hash", "HGETEX"), func() {
2866+
SkipBeforeRedisVersion(7.9, "requires Redis 8.x")
2867+
2868+
err := client.HSet(ctx, "myhash", "f1", "val1", "f2", "val2").Err()
2869+
Expect(err).NotTo(HaveOccurred())
2870+
2871+
// Call HGETEX with EX option and 60 seconds TTL.
2872+
opt := redis.HGetEXOptions{
2873+
ExpirationType: redis.HGetEXExpirationEX,
2874+
ExpirationVal: 60,
2875+
}
2876+
res, err := client.HGetEXWithArgs(ctx, "myhash", &opt, "f1", "f2").Result()
2877+
Expect(err).NotTo(HaveOccurred())
2878+
Expect(res).To(Equal([]string{"val1", "val2"}))
2879+
})
2880+
2881+
It("should HGETEX with PERSIST option", Label("hash", "HGETEX"), func() {
2882+
SkipBeforeRedisVersion(7.9, "requires Redis 8.x")
2883+
2884+
err := client.HSet(ctx, "myhash", "f1", "val1", "f2", "val2").Err()
2885+
Expect(err).NotTo(HaveOccurred())
2886+
2887+
// Call HGETEX with PERSIST (no TTL value needed).
2888+
opt := redis.HGetEXOptions{ExpirationType: redis.HGetEXExpirationPERSIST}
2889+
res, err := client.HGetEXWithArgs(ctx, "myhash", &opt, "f1", "f2").Result()
2890+
Expect(err).NotTo(HaveOccurred())
2891+
Expect(res).To(Equal([]string{"val1", "val2"}))
2892+
})
2893+
2894+
It("should HGETEX with EXAT option", Label("hash", "HGETEX"), func() {
2895+
SkipBeforeRedisVersion(7.9, "requires Redis 8.x")
2896+
2897+
err := client.HSet(ctx, "myhash", "f1", "val1", "f2", "val2").Err()
2898+
Expect(err).NotTo(HaveOccurred())
2899+
2900+
// Set expiration at a specific Unix timestamp (60 seconds from now).
2901+
expireAt := time.Now().Add(60 * time.Second).Unix()
2902+
opt := redis.HGetEXOptions{
2903+
ExpirationType: redis.HGetEXExpirationEXAT,
2904+
ExpirationVal: expireAt,
2905+
}
2906+
res, err := client.HGetEXWithArgs(ctx, "myhash", &opt, "f1", "f2").Result()
2907+
Expect(err).NotTo(HaveOccurred())
2908+
Expect(res).To(Equal([]string{"val1", "val2"}))
2909+
})
2910+
2911+
// -----------------------------
2912+
// HSETEX with FNX/FXX options
2913+
// -----------------------------
2914+
It("should HSETEX with FNX condition", Label("hash", "HSETEX"), func() {
2915+
SkipBeforeRedisVersion(7.9, "requires Redis 8.x")
2916+
2917+
opt := redis.HSetEXOptions{
2918+
Condition: redis.HSetEXFNX,
2919+
ExpirationType: redis.HSetEXExpirationEX,
2920+
ExpirationVal: 60,
2921+
}
2922+
res, err := client.HSetEXWithArgs(ctx, "myhash", &opt, "f1", "val1").Result()
2923+
Expect(err).NotTo(HaveOccurred())
2924+
Expect(res).To(Equal(int64(1)))
2925+
2926+
opt = redis.HSetEXOptions{
2927+
Condition: redis.HSetEXFNX,
2928+
ExpirationType: redis.HSetEXExpirationEX,
2929+
ExpirationVal: 60,
2930+
}
2931+
res, err = client.HSetEXWithArgs(ctx, "myhash", &opt, "f1", "val2").Result()
2932+
Expect(err).NotTo(HaveOccurred())
2933+
Expect(res).To(Equal(int64(0)))
2934+
})
2935+
2936+
It("should HSETEX with FXX condition", Label("hash", "HSETEX"), func() {
2937+
SkipBeforeRedisVersion(7.9, "requires Redis 8.x")
2938+
2939+
err := client.HSet(ctx, "myhash", "f2", "val1").Err()
2940+
Expect(err).NotTo(HaveOccurred())
2941+
2942+
opt := redis.HSetEXOptions{
2943+
Condition: redis.HSetEXFXX,
2944+
ExpirationType: redis.HSetEXExpirationEX,
2945+
ExpirationVal: 60,
2946+
}
2947+
res, err := client.HSetEXWithArgs(ctx, "myhash", &opt, "f2", "val2").Result()
2948+
Expect(err).NotTo(HaveOccurred())
2949+
Expect(res).To(Equal(int64(1)))
2950+
opt = redis.HSetEXOptions{
2951+
Condition: redis.HSetEXFXX,
2952+
ExpirationType: redis.HSetEXExpirationEX,
2953+
ExpirationVal: 60,
2954+
}
2955+
res, err = client.HSetEXWithArgs(ctx, "myhash", &opt, "f3", "val3").Result()
2956+
Expect(err).NotTo(HaveOccurred())
2957+
Expect(res).To(Equal(int64(0)))
2958+
})
2959+
2960+
It("should HSETEX with multiple field operations", Label("hash", "HSETEX"), func() {
2961+
SkipBeforeRedisVersion(7.9, "requires Redis 8.x")
2962+
2963+
opt := redis.HSetEXOptions{
2964+
ExpirationType: redis.HSetEXExpirationEX,
2965+
ExpirationVal: 60,
2966+
}
2967+
res, err := client.HSetEXWithArgs(ctx, "myhash", &opt, "f1", "val1", "f2", "val2").Result()
2968+
Expect(err).NotTo(HaveOccurred())
2969+
Expect(res).To(Equal(int64(1)))
2970+
2971+
values, err := client.HMGet(ctx, "myhash", "f1", "f2").Result()
2972+
Expect(err).NotTo(HaveOccurred())
2973+
Expect(values).To(Equal([]interface{}{"val1", "val2"}))
2974+
})
28282975
})
28292976

28302977
Describe("hyperloglog", func() {

0 commit comments

Comments
 (0)
Please sign in to comment.