1 package redis_test
2
3 import (
4 "bytes"
5 "context"
6 "fmt"
7 "strconv"
8 "strings"
9 "sync"
10 "testing"
11 "time"
12
13 "github.com/redis/go-redis/v9"
14 )
15
16 func benchmarkRedisClient(ctx context.Context, poolSize int) *redis.Client {
17 client := redis.NewClient(&redis.Options{
18 Addr: ":6379",
19 DialTimeout: time.Second,
20 ReadTimeout: time.Second,
21 WriteTimeout: time.Second,
22 PoolSize: poolSize,
23 })
24 if err := client.FlushDB(ctx).Err(); err != nil {
25 panic(err)
26 }
27 return client
28 }
29
30 func BenchmarkRedisPing(b *testing.B) {
31 ctx := context.Background()
32 rdb := benchmarkRedisClient(ctx, 10)
33 defer rdb.Close()
34
35 b.ResetTimer()
36
37 b.RunParallel(func(pb *testing.PB) {
38 for pb.Next() {
39 if err := rdb.Ping(ctx).Err(); err != nil {
40 b.Fatal(err)
41 }
42 }
43 })
44 }
45
46 func BenchmarkSetGoroutines(b *testing.B) {
47 ctx := context.Background()
48 rdb := benchmarkRedisClient(ctx, 10)
49 defer rdb.Close()
50
51 for i := 0; i < b.N; i++ {
52 var wg sync.WaitGroup
53
54 for i := 0; i < 1000; i++ {
55 wg.Add(1)
56 go func() {
57 defer wg.Done()
58
59 err := rdb.Set(ctx, "hello", "world", 0).Err()
60 if err != nil {
61 panic(err)
62 }
63 }()
64 }
65
66 wg.Wait()
67 }
68 }
69
70 func BenchmarkRedisGetNil(b *testing.B) {
71 ctx := context.Background()
72 client := benchmarkRedisClient(ctx, 10)
73 defer client.Close()
74
75 b.ResetTimer()
76
77 b.RunParallel(func(pb *testing.PB) {
78 for pb.Next() {
79 if err := client.Get(ctx, "key").Err(); err != redis.Nil {
80 b.Fatal(err)
81 }
82 }
83 })
84 }
85
86 type setStringBenchmark struct {
87 poolSize int
88 valueSize int
89 }
90
91 func (bm setStringBenchmark) String() string {
92 return fmt.Sprintf("pool=%d value=%d", bm.poolSize, bm.valueSize)
93 }
94
95 func BenchmarkRedisSetString(b *testing.B) {
96 benchmarks := []setStringBenchmark{
97 {10, 64},
98 {10, 1024},
99 {10, 64 * 1024},
100 {10, 1024 * 1024},
101 {10, 10 * 1024 * 1024},
102
103 {100, 64},
104 {100, 1024},
105 {100, 64 * 1024},
106 {100, 1024 * 1024},
107 {100, 10 * 1024 * 1024},
108 }
109 for _, bm := range benchmarks {
110 b.Run(bm.String(), func(b *testing.B) {
111 ctx := context.Background()
112 client := benchmarkRedisClient(ctx, bm.poolSize)
113 defer client.Close()
114
115 value := strings.Repeat("1", bm.valueSize)
116
117 b.ResetTimer()
118
119 b.RunParallel(func(pb *testing.PB) {
120 for pb.Next() {
121 err := client.Set(ctx, "key", value, 0).Err()
122 if err != nil {
123 b.Fatal(err)
124 }
125 }
126 })
127 })
128 }
129 }
130
131 func BenchmarkRedisSetGetBytes(b *testing.B) {
132 ctx := context.Background()
133 client := benchmarkRedisClient(ctx, 10)
134 defer client.Close()
135
136 value := bytes.Repeat([]byte{'1'}, 10000)
137
138 b.ResetTimer()
139
140 b.RunParallel(func(pb *testing.PB) {
141 for pb.Next() {
142 if err := client.Set(ctx, "key", value, 0).Err(); err != nil {
143 b.Fatal(err)
144 }
145
146 got, err := client.Get(ctx, "key").Bytes()
147 if err != nil {
148 b.Fatal(err)
149 }
150 if !bytes.Equal(got, value) {
151 b.Fatalf("got != value")
152 }
153 }
154 })
155 }
156
157 func BenchmarkRedisMGet(b *testing.B) {
158 ctx := context.Background()
159 client := benchmarkRedisClient(ctx, 10)
160 defer client.Close()
161
162 if err := client.MSet(ctx, "key1", "hello1", "key2", "hello2").Err(); err != nil {
163 b.Fatal(err)
164 }
165
166 b.ResetTimer()
167
168 b.RunParallel(func(pb *testing.PB) {
169 for pb.Next() {
170 if err := client.MGet(ctx, "key1", "key2").Err(); err != nil {
171 b.Fatal(err)
172 }
173 }
174 })
175 }
176
177 func BenchmarkSetExpire(b *testing.B) {
178 ctx := context.Background()
179 client := benchmarkRedisClient(ctx, 10)
180 defer client.Close()
181
182 b.ResetTimer()
183
184 b.RunParallel(func(pb *testing.PB) {
185 for pb.Next() {
186 if err := client.Set(ctx, "key", "hello", 0).Err(); err != nil {
187 b.Fatal(err)
188 }
189 if err := client.Expire(ctx, "key", time.Second).Err(); err != nil {
190 b.Fatal(err)
191 }
192 }
193 })
194 }
195
196 func BenchmarkPipeline(b *testing.B) {
197 ctx := context.Background()
198 client := benchmarkRedisClient(ctx, 10)
199 defer client.Close()
200
201 b.ResetTimer()
202
203 b.RunParallel(func(pb *testing.PB) {
204 for pb.Next() {
205 _, err := client.Pipelined(ctx, func(pipe redis.Pipeliner) error {
206 pipe.Set(ctx, "key", "hello", 0)
207 pipe.Expire(ctx, "key", time.Second)
208 return nil
209 })
210 if err != nil {
211 b.Fatal(err)
212 }
213 }
214 })
215 }
216
217 func BenchmarkZAdd(b *testing.B) {
218 ctx := context.Background()
219 client := benchmarkRedisClient(ctx, 10)
220 defer client.Close()
221
222 b.ResetTimer()
223
224 b.RunParallel(func(pb *testing.PB) {
225 for pb.Next() {
226 err := client.ZAdd(ctx, "key", redis.Z{
227 Score: float64(1),
228 Member: "hello",
229 }).Err()
230 if err != nil {
231 b.Fatal(err)
232 }
233 }
234 })
235 }
236
237 func BenchmarkXRead(b *testing.B) {
238 ctx := context.Background()
239 client := benchmarkRedisClient(ctx, 10)
240 defer client.Close()
241
242 args := redis.XAddArgs{
243 Stream: "1",
244 ID: "*",
245 Values: map[string]string{"uno": "dos"},
246 }
247
248 lenStreams := 16
249 streams := make([]string, 0, lenStreams)
250 for i := 0; i < lenStreams; i++ {
251 streams = append(streams, strconv.Itoa(i))
252 }
253 for i := 0; i < lenStreams; i++ {
254 streams = append(streams, "0")
255 }
256
257 b.ReportAllocs()
258 b.ResetTimer()
259
260 b.RunParallel(func(pb *testing.PB) {
261 for pb.Next() {
262 client.XAdd(ctx, &args)
263
264 err := client.XRead(ctx, &redis.XReadArgs{
265 Streams: streams,
266 Count: 1,
267 Block: time.Second,
268 }).Err()
269 if err != nil {
270 b.Fatal(err)
271 }
272 }
273 })
274 }
275
276
277
278 func newClusterScenario() *clusterScenario {
279 return &clusterScenario{
280 ports: []string{"16600", "16601", "16602", "16603", "16604", "16605"},
281 nodeIDs: make([]string, 6),
282 clients: make(map[string]*redis.Client, 6),
283 }
284 }
285
286 var clusterBench *clusterScenario
287
288 func BenchmarkClusterPing(b *testing.B) {
289 if testing.Short() {
290 b.Skip("skipping in short mode")
291 }
292
293 ctx := context.Background()
294 if clusterBench == nil {
295 clusterBench = newClusterScenario()
296 if err := configureClusterTopology(ctx, clusterBench); err != nil {
297 b.Fatal(err)
298 }
299 }
300
301 client := clusterBench.newClusterClient(ctx, redisClusterOptions())
302 defer client.Close()
303
304 b.Run("cluster ping", func(b *testing.B) {
305 b.ResetTimer()
306
307 b.RunParallel(func(pb *testing.PB) {
308 for pb.Next() {
309 err := client.Ping(ctx).Err()
310 if err != nil {
311 b.Fatal(err)
312 }
313 }
314 })
315 })
316 }
317
318 func BenchmarkClusterDoInt(b *testing.B) {
319 if testing.Short() {
320 b.Skip("skipping in short mode")
321 }
322
323 ctx := context.Background()
324 if clusterBench == nil {
325 clusterBench = newClusterScenario()
326 if err := configureClusterTopology(ctx, clusterBench); err != nil {
327 b.Fatal(err)
328 }
329 }
330
331 client := clusterBench.newClusterClient(ctx, redisClusterOptions())
332 defer client.Close()
333
334 b.Run("cluster do set int", func(b *testing.B) {
335 b.ResetTimer()
336 b.RunParallel(func(pb *testing.PB) {
337 for pb.Next() {
338 err := client.Do(ctx, "SET", 10, 10).Err()
339 if err != nil {
340 b.Fatal(err)
341 }
342 }
343 })
344 })
345 }
346
347 func BenchmarkClusterSetString(b *testing.B) {
348 if testing.Short() {
349 b.Skip("skipping in short mode")
350 }
351
352 ctx := context.Background()
353 if clusterBench == nil {
354 clusterBench = newClusterScenario()
355 if err := configureClusterTopology(ctx, clusterBench); err != nil {
356 b.Fatal(err)
357 }
358 }
359
360 client := clusterBench.newClusterClient(ctx, redisClusterOptions())
361 defer client.Close()
362
363 value := string(bytes.Repeat([]byte{'1'}, 10000))
364
365 b.Run("cluster set string", func(b *testing.B) {
366 b.ResetTimer()
367
368 b.RunParallel(func(pb *testing.PB) {
369 for pb.Next() {
370 err := client.Set(ctx, "key", value, 0).Err()
371 if err != nil {
372 b.Fatal(err)
373 }
374 }
375 })
376 })
377 }
378
379 func BenchmarkExecRingSetAddrsCmd(b *testing.B) {
380 const (
381 ringShard1Name = "ringShardOne"
382 ringShard2Name = "ringShardTwo"
383 )
384
385 ring := redis.NewRing(&redis.RingOptions{
386 Addrs: map[string]string{
387 "ringShardOne": ":" + ringShard1Port,
388 },
389 NewClient: func(opt *redis.Options) *redis.Client {
390
391 time.Sleep(100 * time.Millisecond)
392 return redis.NewClient(opt)
393 },
394 })
395 defer ring.Close()
396
397 if _, err := ring.Ping(context.Background()).Result(); err != nil {
398 b.Fatal(err)
399 }
400
401
402 updatesDone := make(chan struct{})
403 defer func() { close(updatesDone) }()
404 go func() {
405 ticker := time.NewTicker(10 * time.Millisecond)
406 defer ticker.Stop()
407 for i := 0; ; i++ {
408 select {
409 case <-ticker.C:
410 if i%2 == 0 {
411 ring.SetAddrs(map[string]string{
412 ringShard1Name: ":" + ringShard1Port,
413 })
414 } else {
415 ring.SetAddrs(map[string]string{
416 ringShard1Name: ":" + ringShard1Port,
417 ringShard2Name: ":" + ringShard2Port,
418 })
419 }
420 case <-updatesDone:
421 return
422 }
423 }
424 }()
425
426 b.ResetTimer()
427 for i := 0; i < b.N; i++ {
428 if _, err := ring.Ping(context.Background()).Result(); err != nil {
429 if err == redis.ErrClosed {
430
431 continue
432 } else {
433 b.Fatal(err)
434 }
435 }
436 }
437 }
438
View as plain text