...

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

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

     1  package redis_test
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"sync"
     8  	"time"
     9  
    10  	"github.com/redis/go-redis/v9"
    11  )
    12  
    13  var (
    14  	ctx = context.Background()
    15  	rdb *redis.Client
    16  )
    17  
    18  func init() {
    19  	rdb = redis.NewClient(&redis.Options{
    20  		Addr:         ":6379",
    21  		DialTimeout:  10 * time.Second,
    22  		ReadTimeout:  30 * time.Second,
    23  		WriteTimeout: 30 * time.Second,
    24  		PoolSize:     10,
    25  		PoolTimeout:  30 * time.Second,
    26  	})
    27  }
    28  
    29  func ExampleNewClient() {
    30  	rdb := redis.NewClient(&redis.Options{
    31  		Addr:     "localhost:6379", // use default Addr
    32  		Password: "",               // no password set
    33  		DB:       0,                // use default DB
    34  	})
    35  
    36  	pong, err := rdb.Ping(ctx).Result()
    37  	fmt.Println(pong, err)
    38  	// Output: PONG <nil>
    39  }
    40  
    41  func ExampleParseURL() {
    42  	opt, err := redis.ParseURL("redis://:qwerty@localhost:6379/1?dial_timeout=5s")
    43  	if err != nil {
    44  		panic(err)
    45  	}
    46  	fmt.Println("addr is", opt.Addr)
    47  	fmt.Println("db is", opt.DB)
    48  	fmt.Println("password is", opt.Password)
    49  	fmt.Println("dial timeout is", opt.DialTimeout)
    50  
    51  	// Create client as usually.
    52  	_ = redis.NewClient(opt)
    53  
    54  	// Output: addr is localhost:6379
    55  	// db is 1
    56  	// password is qwerty
    57  	// dial timeout is 5s
    58  }
    59  
    60  func ExampleNewFailoverClient() {
    61  	// See http://redis.io/topics/sentinel for instructions how to
    62  	// setup Redis Sentinel.
    63  	rdb := redis.NewFailoverClient(&redis.FailoverOptions{
    64  		MasterName:    "master",
    65  		SentinelAddrs: []string{":26379"},
    66  	})
    67  	rdb.Ping(ctx)
    68  }
    69  
    70  func ExampleNewClusterClient() {
    71  	// See http://redis.io/topics/cluster-tutorial for instructions
    72  	// how to setup Redis Cluster.
    73  	rdb := redis.NewClusterClient(&redis.ClusterOptions{
    74  		Addrs: []string{":7000", ":7001", ":7002", ":7003", ":7004", ":7005"},
    75  	})
    76  	rdb.Ping(ctx)
    77  }
    78  
    79  // Following example creates a cluster from 2 master nodes and 2 slave nodes
    80  // without using cluster mode or Redis Sentinel.
    81  func ExampleNewClusterClient_manualSetup() {
    82  	// clusterSlots returns cluster slots information.
    83  	// It can use service like ZooKeeper to maintain configuration information
    84  	// and Cluster.ReloadState to manually trigger state reloading.
    85  	clusterSlots := func(ctx context.Context) ([]redis.ClusterSlot, error) {
    86  		slots := []redis.ClusterSlot{
    87  			// First node with 1 master and 1 slave.
    88  			{
    89  				Start: 0,
    90  				End:   8191,
    91  				Nodes: []redis.ClusterNode{{
    92  					Addr: ":7000", // master
    93  				}, {
    94  					Addr: ":8000", // 1st slave
    95  				}},
    96  			},
    97  			// Second node with 1 master and 1 slave.
    98  			{
    99  				Start: 8192,
   100  				End:   16383,
   101  				Nodes: []redis.ClusterNode{{
   102  					Addr: ":7001", // master
   103  				}, {
   104  					Addr: ":8001", // 1st slave
   105  				}},
   106  			},
   107  		}
   108  		return slots, nil
   109  	}
   110  
   111  	rdb := redis.NewClusterClient(&redis.ClusterOptions{
   112  		ClusterSlots:  clusterSlots,
   113  		RouteRandomly: true,
   114  	})
   115  	rdb.Ping(ctx)
   116  
   117  	// ReloadState reloads cluster state. It calls ClusterSlots func
   118  	// to get cluster slots information.
   119  	rdb.ReloadState(ctx)
   120  }
   121  
   122  func ExampleNewRing() {
   123  	rdb := redis.NewRing(&redis.RingOptions{
   124  		Addrs: map[string]string{
   125  			"shard1": ":7000",
   126  			"shard2": ":7001",
   127  			"shard3": ":7002",
   128  		},
   129  	})
   130  	rdb.Ping(ctx)
   131  }
   132  
   133  func ExampleClient() {
   134  	err := rdb.Set(ctx, "key", "value", 0).Err()
   135  	if err != nil {
   136  		panic(err)
   137  	}
   138  
   139  	val, err := rdb.Get(ctx, "key").Result()
   140  	if err != nil {
   141  		panic(err)
   142  	}
   143  	fmt.Println("key", val)
   144  
   145  	val2, err := rdb.Get(ctx, "missing_key").Result()
   146  	if err == redis.Nil {
   147  		fmt.Println("missing_key does not exist")
   148  	} else if err != nil {
   149  		panic(err)
   150  	} else {
   151  		fmt.Println("missing_key", val2)
   152  	}
   153  	// Output: key value
   154  	// missing_key does not exist
   155  }
   156  
   157  func ExampleConn_name() {
   158  	conn := rdb.Conn()
   159  
   160  	err := conn.ClientSetName(ctx, "foobar").Err()
   161  	if err != nil {
   162  		panic(err)
   163  	}
   164  
   165  	// Open other connections.
   166  	for i := 0; i < 10; i++ {
   167  		go rdb.Ping(ctx)
   168  	}
   169  
   170  	s, err := conn.ClientGetName(ctx).Result()
   171  	if err != nil {
   172  		panic(err)
   173  	}
   174  	fmt.Println(s)
   175  	// Output: foobar
   176  }
   177  
   178  func ExampleConn_client_setInfo_libraryVersion() {
   179  	conn := rdb.Conn()
   180  
   181  	err := conn.ClientSetInfo(ctx, redis.WithLibraryVersion("1.2.3")).Err()
   182  	if err != nil {
   183  		panic(err)
   184  	}
   185  
   186  	// Open other connections.
   187  	for i := 0; i < 10; i++ {
   188  		go rdb.Ping(ctx)
   189  	}
   190  
   191  	s, err := conn.ClientInfo(ctx).Result()
   192  	if err != nil {
   193  		panic(err)
   194  	}
   195  
   196  	fmt.Println(s.LibVer)
   197  	// Output: 1.2.3
   198  }
   199  
   200  func ExampleClient_Set() {
   201  	// Last argument is expiration. Zero means the key has no
   202  	// expiration time.
   203  	err := rdb.Set(ctx, "key", "value", 0).Err()
   204  	if err != nil {
   205  		panic(err)
   206  	}
   207  
   208  	// key2 will expire in an hour.
   209  	err = rdb.Set(ctx, "key2", "value", time.Hour).Err()
   210  	if err != nil {
   211  		panic(err)
   212  	}
   213  }
   214  
   215  func ExampleClient_SetEx() {
   216  	err := rdb.SetEx(ctx, "key", "value", time.Hour).Err()
   217  	if err != nil {
   218  		panic(err)
   219  	}
   220  }
   221  
   222  func ExampleClient_HSet() {
   223  	// Set "redis" tag for hash key
   224  	type ExampleUser struct {
   225  		Name string `redis:"name"`
   226  		Age  int    `redis:"age"`
   227  	}
   228  
   229  	items := ExampleUser{"jane", 22}
   230  
   231  	err := rdb.HSet(ctx, "user:1", items).Err()
   232  	if err != nil {
   233  		panic(err)
   234  	}
   235  }
   236  
   237  func ExampleClient_Incr() {
   238  	result, err := rdb.Incr(ctx, "counter").Result()
   239  	if err != nil {
   240  		panic(err)
   241  	}
   242  
   243  	fmt.Println(result)
   244  	// Output: 1
   245  }
   246  
   247  func ExampleClient_BLPop() {
   248  	if err := rdb.RPush(ctx, "queue", "message").Err(); err != nil {
   249  		panic(err)
   250  	}
   251  
   252  	// use `rdb.BLPop(ctx, 0, "queue")` for infinite waiting time
   253  	result, err := rdb.BLPop(ctx, 1*time.Second, "queue").Result()
   254  	if err != nil {
   255  		panic(err)
   256  	}
   257  
   258  	fmt.Println(result[0], result[1])
   259  	// Output: queue message
   260  }
   261  
   262  func ExampleClient_Scan() {
   263  	rdb.FlushDB(ctx)
   264  	for i := 0; i < 33; i++ {
   265  		err := rdb.Set(ctx, fmt.Sprintf("key%d", i), "value", 0).Err()
   266  		if err != nil {
   267  			panic(err)
   268  		}
   269  	}
   270  
   271  	var cursor uint64
   272  	var n int
   273  	for {
   274  		var keys []string
   275  		var err error
   276  		keys, cursor, err = rdb.Scan(ctx, cursor, "key*", 10).Result()
   277  		if err != nil {
   278  			panic(err)
   279  		}
   280  		n += len(keys)
   281  		if cursor == 0 {
   282  			break
   283  		}
   284  	}
   285  
   286  	fmt.Printf("found %d keys\n", n)
   287  	// Output: found 33 keys
   288  }
   289  
   290  func ExampleClient_ScanType() {
   291  	rdb.FlushDB(ctx)
   292  	for i := 0; i < 33; i++ {
   293  		err := rdb.Set(ctx, fmt.Sprintf("key%d", i), "value", 0).Err()
   294  		if err != nil {
   295  			panic(err)
   296  		}
   297  	}
   298  
   299  	var cursor uint64
   300  	var n int
   301  	for {
   302  		var keys []string
   303  		var err error
   304  		keys, cursor, err = rdb.ScanType(ctx, cursor, "key*", 10, "string").Result()
   305  		if err != nil {
   306  			panic(err)
   307  		}
   308  		n += len(keys)
   309  		if cursor == 0 {
   310  			break
   311  		}
   312  	}
   313  
   314  	fmt.Printf("found %d keys\n", n)
   315  	// Output: found 33 keys
   316  }
   317  
   318  // ExampleClient_ScanType_hashType uses the keyType "hash".
   319  func ExampleClient_ScanType_hashType() {
   320  	rdb.FlushDB(ctx)
   321  	for i := 0; i < 33; i++ {
   322  		err := rdb.HSet(context.TODO(), fmt.Sprintf("key%d", i), "value", "foo").Err()
   323  		if err != nil {
   324  			panic(err)
   325  		}
   326  	}
   327  
   328  	var allKeys []string
   329  	var cursor uint64
   330  	var err error
   331  
   332  	for {
   333  		var keysFromScan []string
   334  		keysFromScan, cursor, err = rdb.ScanType(context.TODO(), cursor, "key*", 10, "hash").Result()
   335  		if err != nil {
   336  			panic(err)
   337  		}
   338  		allKeys = append(allKeys, keysFromScan...)
   339  		if cursor == 0 {
   340  			break
   341  		}
   342  	}
   343  	fmt.Printf("%d keys ready for use", len(allKeys))
   344  	// Output: 33 keys ready for use
   345  }
   346  
   347  // ExampleMapStringStringCmd_Scan shows how to scan the results of a map fetch
   348  // into a struct.
   349  func ExampleMapStringStringCmd_Scan() {
   350  	rdb.FlushDB(ctx)
   351  	err := rdb.HMSet(ctx, "map",
   352  		"name", "hello",
   353  		"count", 123,
   354  		"correct", true).Err()
   355  	if err != nil {
   356  		panic(err)
   357  	}
   358  
   359  	// Get the map. The same approach works for HmGet().
   360  	res := rdb.HGetAll(ctx, "map")
   361  	if res.Err() != nil {
   362  		panic(res.Err())
   363  	}
   364  
   365  	type data struct {
   366  		Name    string `redis:"name"`
   367  		Count   int    `redis:"count"`
   368  		Correct bool   `redis:"correct"`
   369  	}
   370  
   371  	// Scan the results into the struct.
   372  	var d data
   373  	if err := res.Scan(&d); err != nil {
   374  		panic(err)
   375  	}
   376  
   377  	fmt.Println(d)
   378  	// Output: {hello 123 true}
   379  }
   380  
   381  // ExampleSliceCmd_Scan shows how to scan the results of a multi key fetch
   382  // into a struct.
   383  func ExampleSliceCmd_Scan() {
   384  	rdb.FlushDB(ctx)
   385  	err := rdb.MSet(ctx,
   386  		"name", "hello",
   387  		"count", 123,
   388  		"correct", true).Err()
   389  	if err != nil {
   390  		panic(err)
   391  	}
   392  
   393  	res := rdb.MGet(ctx, "name", "count", "empty", "correct")
   394  	if res.Err() != nil {
   395  		panic(res.Err())
   396  	}
   397  
   398  	type data struct {
   399  		Name    string `redis:"name"`
   400  		Count   int    `redis:"count"`
   401  		Correct bool   `redis:"correct"`
   402  	}
   403  
   404  	// Scan the results into the struct.
   405  	var d data
   406  	if err := res.Scan(&d); err != nil {
   407  		panic(err)
   408  	}
   409  
   410  	fmt.Println(d)
   411  	// Output: {hello 123 true}
   412  }
   413  
   414  func ExampleClient_Pipelined() {
   415  	var incr *redis.IntCmd
   416  	_, err := rdb.Pipelined(ctx, func(pipe redis.Pipeliner) error {
   417  		incr = pipe.Incr(ctx, "pipelined_counter")
   418  		pipe.Expire(ctx, "pipelined_counter", time.Hour)
   419  		return nil
   420  	})
   421  	fmt.Println(incr.Val(), err)
   422  	// Output: 1 <nil>
   423  }
   424  
   425  func ExampleClient_Pipeline() {
   426  	pipe := rdb.Pipeline()
   427  
   428  	incr := pipe.Incr(ctx, "pipeline_counter")
   429  	pipe.Expire(ctx, "pipeline_counter", time.Hour)
   430  
   431  	// Execute
   432  	//
   433  	//     INCR pipeline_counter
   434  	//     EXPIRE pipeline_counts 3600
   435  	//
   436  	// using one rdb-server roundtrip.
   437  	_, err := pipe.Exec(ctx)
   438  	fmt.Println(incr.Val(), err)
   439  	// Output: 1 <nil>
   440  }
   441  
   442  func ExampleClient_TxPipelined() {
   443  	var incr *redis.IntCmd
   444  	_, err := rdb.TxPipelined(ctx, func(pipe redis.Pipeliner) error {
   445  		incr = pipe.Incr(ctx, "tx_pipelined_counter")
   446  		pipe.Expire(ctx, "tx_pipelined_counter", time.Hour)
   447  		return nil
   448  	})
   449  	fmt.Println(incr.Val(), err)
   450  	// Output: 1 <nil>
   451  }
   452  
   453  func ExampleClient_TxPipeline() {
   454  	pipe := rdb.TxPipeline()
   455  
   456  	incr := pipe.Incr(ctx, "tx_pipeline_counter")
   457  	pipe.Expire(ctx, "tx_pipeline_counter", time.Hour)
   458  
   459  	// Execute
   460  	//
   461  	//     MULTI
   462  	//     INCR pipeline_counter
   463  	//     EXPIRE pipeline_counts 3600
   464  	//     EXEC
   465  	//
   466  	// using one rdb-server roundtrip.
   467  	_, err := pipe.Exec(ctx)
   468  	fmt.Println(incr.Val(), err)
   469  	// Output: 1 <nil>
   470  }
   471  
   472  func ExampleClient_Watch() {
   473  	const maxRetries = 10000
   474  
   475  	// Increment transactionally increments key using GET and SET commands.
   476  	increment := func(key string) error {
   477  		// Transactional function.
   478  		txf := func(tx *redis.Tx) error {
   479  			// Get current value or zero.
   480  			n, err := tx.Get(ctx, key).Int()
   481  			if err != nil && err != redis.Nil {
   482  				return err
   483  			}
   484  
   485  			// Actual operation (local in optimistic lock).
   486  			n++
   487  
   488  			// Operation is committed only if the watched keys remain unchanged.
   489  			_, err = tx.TxPipelined(ctx, func(pipe redis.Pipeliner) error {
   490  				pipe.Set(ctx, key, n, 0)
   491  				return nil
   492  			})
   493  			return err
   494  		}
   495  
   496  		for i := 0; i < maxRetries; i++ {
   497  			err := rdb.Watch(ctx, txf, key)
   498  			if err == nil {
   499  				// Success.
   500  				return nil
   501  			}
   502  			if err == redis.TxFailedErr {
   503  				// Optimistic lock lost. Retry.
   504  				continue
   505  			}
   506  			// Return any other error.
   507  			return err
   508  		}
   509  
   510  		return errors.New("increment reached maximum number of retries")
   511  	}
   512  
   513  	var wg sync.WaitGroup
   514  	for i := 0; i < 100; i++ {
   515  		wg.Add(1)
   516  		go func() {
   517  			defer wg.Done()
   518  
   519  			if err := increment("counter3"); err != nil {
   520  				fmt.Println("increment error:", err)
   521  			}
   522  		}()
   523  	}
   524  	wg.Wait()
   525  
   526  	n, err := rdb.Get(ctx, "counter3").Int()
   527  	fmt.Println("ended with", n, err)
   528  	// Output: ended with 100 <nil>
   529  }
   530  
   531  func ExamplePubSub() {
   532  	pubsub := rdb.Subscribe(ctx, "mychannel1")
   533  
   534  	// Wait for confirmation that subscription is created before publishing anything.
   535  	_, err := pubsub.Receive(ctx)
   536  	if err != nil {
   537  		panic(err)
   538  	}
   539  
   540  	// Go channel which receives messages.
   541  	ch := pubsub.Channel()
   542  
   543  	// Publish a message.
   544  	err = rdb.Publish(ctx, "mychannel1", "hello").Err()
   545  	if err != nil {
   546  		panic(err)
   547  	}
   548  
   549  	time.AfterFunc(time.Second, func() {
   550  		// When pubsub is closed channel is closed too.
   551  		_ = pubsub.Close()
   552  	})
   553  
   554  	// Consume messages.
   555  	for msg := range ch {
   556  		fmt.Println(msg.Channel, msg.Payload)
   557  	}
   558  
   559  	// Output: mychannel1 hello
   560  }
   561  
   562  func ExamplePubSub_Receive() {
   563  	pubsub := rdb.Subscribe(ctx, "mychannel2")
   564  	defer pubsub.Close()
   565  
   566  	for i := 0; i < 2; i++ {
   567  		// ReceiveTimeout is a low level API. Use ReceiveMessage instead.
   568  		msgi, err := pubsub.ReceiveTimeout(ctx, time.Second)
   569  		if err != nil {
   570  			break
   571  		}
   572  
   573  		switch msg := msgi.(type) {
   574  		case *redis.Subscription:
   575  			fmt.Println("subscribed to", msg.Channel)
   576  
   577  			_, err := rdb.Publish(ctx, "mychannel2", "hello").Result()
   578  			if err != nil {
   579  				panic(err)
   580  			}
   581  		case *redis.Message:
   582  			fmt.Println("received", msg.Payload, "from", msg.Channel)
   583  		default:
   584  			panic("unreached")
   585  		}
   586  	}
   587  
   588  	// sent message to 1 rdb
   589  	// received hello from mychannel2
   590  }
   591  
   592  func ExampleScript() {
   593  	IncrByXX := redis.NewScript(`
   594  		if redis.call("GET", KEYS[1]) ~= false then
   595  			return redis.call("INCRBY", KEYS[1], ARGV[1])
   596  		end
   597  		return false
   598  	`)
   599  
   600  	n, err := IncrByXX.Run(ctx, rdb, []string{"xx_counter"}, 2).Result()
   601  	fmt.Println(n, err)
   602  
   603  	err = rdb.Set(ctx, "xx_counter", "40", 0).Err()
   604  	if err != nil {
   605  		panic(err)
   606  	}
   607  
   608  	n, err = IncrByXX.Run(ctx, rdb, []string{"xx_counter"}, 2).Result()
   609  	fmt.Println(n, err)
   610  
   611  	// Output: <nil> redis: nil
   612  	// 42 <nil>
   613  }
   614  
   615  func Example_customCommand() {
   616  	Get := func(ctx context.Context, rdb *redis.Client, key string) *redis.StringCmd {
   617  		cmd := redis.NewStringCmd(ctx, "get", key)
   618  		rdb.Process(ctx, cmd)
   619  		return cmd
   620  	}
   621  
   622  	v, err := Get(ctx, rdb, "key_does_not_exist").Result()
   623  	fmt.Printf("%q %s", v, err)
   624  	// Output: "" redis: nil
   625  }
   626  
   627  func Example_customCommand2() {
   628  	v, err := rdb.Do(ctx, "get", "key_does_not_exist").Text()
   629  	fmt.Printf("%q %s", v, err)
   630  	// Output: "" redis: nil
   631  }
   632  
   633  func ExampleScanIterator() {
   634  	iter := rdb.Scan(ctx, 0, "", 0).Iterator()
   635  	for iter.Next(ctx) {
   636  		fmt.Println(iter.Val())
   637  	}
   638  	if err := iter.Err(); err != nil {
   639  		panic(err)
   640  	}
   641  }
   642  
   643  func ExampleScanCmd_Iterator() {
   644  	iter := rdb.Scan(ctx, 0, "", 0).Iterator()
   645  	for iter.Next(ctx) {
   646  		fmt.Println(iter.Val())
   647  	}
   648  	if err := iter.Err(); err != nil {
   649  		panic(err)
   650  	}
   651  }
   652  
   653  func ExampleNewUniversalClient_simple() {
   654  	rdb := redis.NewUniversalClient(&redis.UniversalOptions{
   655  		Addrs: []string{":6379"},
   656  	})
   657  	defer rdb.Close()
   658  
   659  	rdb.Ping(ctx)
   660  }
   661  
   662  func ExampleNewUniversalClient_failover() {
   663  	rdb := redis.NewUniversalClient(&redis.UniversalOptions{
   664  		MasterName: "master",
   665  		Addrs:      []string{":26379"},
   666  	})
   667  	defer rdb.Close()
   668  
   669  	rdb.Ping(ctx)
   670  }
   671  
   672  func ExampleNewUniversalClient_cluster() {
   673  	rdb := redis.NewUniversalClient(&redis.UniversalOptions{
   674  		Addrs: []string{":7000", ":7001", ":7002", ":7003", ":7004", ":7005"},
   675  	})
   676  	defer rdb.Close()
   677  
   678  	rdb.Ping(ctx)
   679  }
   680  
   681  func ExampleClient_SlowLogGet() {
   682  	if RECluster {
   683  		// skip slowlog test for cluster
   684  		fmt.Println(2)
   685  		return
   686  	}
   687  	const key = "slowlog-log-slower-than"
   688  
   689  	old := rdb.ConfigGet(ctx, key).Val()
   690  	rdb.ConfigSet(ctx, key, "0")
   691  	defer rdb.ConfigSet(ctx, key, old[key])
   692  
   693  	if err := rdb.Do(ctx, "slowlog", "reset").Err(); err != nil {
   694  		panic(err)
   695  	}
   696  
   697  	rdb.Set(ctx, "test", "true", 0)
   698  
   699  	result, err := rdb.SlowLogGet(ctx, -1).Result()
   700  	if err != nil {
   701  		panic(err)
   702  	}
   703  	fmt.Println(len(result))
   704  	// Output: 2
   705  }
   706  

View as plain text