...

Source file src/github.com/redis/go-redis/v9/bench_test.go

Documentation: github.com/redis/go-redis/v9

     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  			// Simulate slow shard creation
   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  	// Continuously update addresses by adding and removing one address
   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  				// The shard client could be closed while ping command is in progress
   431  				continue
   432  			} else {
   433  				b.Fatal(err)
   434  			}
   435  		}
   436  	}
   437  }
   438  

View as plain text