rueidis Documentation

repository·main·Indexed 25 days ago

https://github.com/redis/rueidis

A high-performance Golang Redis client featuring auto-pipelining for high throughput and server-assisted client-side caching for low latency. It includes a developer-friendly command builder, a mock package for testing with gomock, and an 'om' package for mapping Go structs to Redis Hashes or RedisJSON with support for RediSearch indexing and optimistic locking.

Tokens
53.4K
Snippets
76
Records
280
Agent score
83%

What's inside rueidis

  1. Run Valkey with the RDMA Module

    main

    After building, start the Valkey server by loading the RDMA module and binding to the RDMA network interface and port.

    Note: Ensure RDMA connectivity is verified between your client and server (e.g., using rping) before starting the server.

    ./src/valkey-server --loadmodule ./src/valkey-rdma.so --rdma-bind <server ip on the rdma netdev> --rdma-port 6378
  2. Use rueidiscompatmock for Go-redis like API Mocking

    main

    The rueidiscompatmock package provides a test helper that mirrors the go-redis/redismock interface. It allows you to set expectations for Redis commands when using the rueidiscompat adapter. You can use Expect* methods to define expected commands and their return values. Expectations are matched in the order they are queued.

    package main
    
    import (
    	"context"
    	"testing"
    
    	"github.com/redis/rueidis/mock"
    	"github.com/redis/rueidis/rueidiscompat"
    	"github.com/redis/rueidis/rueidiscompatmock"
    	"go.uber.org/mock/gomock"
    )
    
    func TestExample(t *testing.T) {
    	ctrl := gomock.NewController(t)
    	m := mock.NewClient(ctrl)
    	compatmock := rueidiscompatmock.NewAdapter(m)
    
    	compatmock.ExpectSet("key", "val", 0).SetVal("OK")
    	compatmock.ExpectGet("key").SetVal("val")
    
    	rdb := rueidiscompat.NewAdapter(m)
    	rdb.Set(context.Background(), "key", "val", 0)
    	rdb.Get(context.Background(), "key")
    }
  3. Initialize a rueidisaside client

    main

    To use the Cache-Aside pattern with Redis Client-Side Caching, initialize a client using rueidisaside.NewClient. You must provide a rueidisaside.ClientOption which embeds a standard rueidis.ClientOption for connection settings like InitAddress.

    client, err := rueidisaside.NewClient(rueidisaside.ClientOption{
    	ClientOption: rueidis.ClientOption{InitAddress: []string{"127.0.0.1:6379"}},
    })
  4. Enable OpenTelemetry Tracing and Connection Metrics with rueidisotel

    main

    Use rueidisotel.NewClient to create a rueidis client that automatically includes OpenTelemetry Tracing and Connection Metrics.

    Built-in Connection Metrics:

    • rueidis_dial_attempt: number of dial attempts
    • rueidis_dial_success: number of successful dials
    • rueidis_dial_conns: number of connections
    • rueidis_dial_latency: dial latency in seconds

    Client-side Caching Metrics:

    • rueidis_do_cache_miss: number of cache misses on client side
    • rueidis_do_cache_hits: number of cache hits on client side

    Client-side Command Metrics:

    • rueidis_command_duration_seconds: histogram of command duration
    • rueidis_command_errors: number of command errors

    Note: rueidisotel.NewClient is not supported on Go 1.18 and Go 1.19 builds.

    package main
    
    import (
        "context"
        "time"
    
        "github.com/redis/rueidis"
        "github.com/redis/rueidis/rueidisotel"
        "go.opentelemetry.io/otel/attribute"
    )
    
    func main() {
        client, err := rueidisotel.NewClient(rueidis.ClientOption{InitAddress: []string{"127.0.0.1:6379"}})
        if err != nil {
            panic(err)
        }
        defer client.Close()
    
        // Basic usage
        ctx := context.Background()
        client.DoCache(ctx, client.B().Get().Key("mykey").Cache(), time.Minute)
    }
  5. Use Pub/Sub with client.Receive()

    main

    To receive messages from channels (supporting SUBSCRIBE, PSUBSCRIBE, and Redis 7.0's SSUBSCRIBE), use client.Receive().

    Best Practices:

    • Avoid Blocking the Pipeline: If your message handler performs heavy processing or issues additional commands, run them in a separate goroutine to avoid blocking the pipeline.
    • Dedicated Connections: If the handler is slow, use client.Receive() inside a client.Dedicated() connection so it doesn't block other concurrent requests sharing the same TCP connection.

    Blocking Behavior: client.Receive() will block until:

    1. An unsubscribe/punsubscribe message is received.
    2. The client is closed (rueidis.ErrClosing).
    3. The context is done (ctx.Err()).
    4. The subscription command fails.
    err = client.Receive(context.Background(), client.B().Subscribe().Channel("ch1", "ch2").Build(), func(msg rueidis.PubSubMessage) {
        // Handle the message.
        // Use a goroutine for heavy work to avoid blocking the pipeline:
        // go func() {
        //     long work or client.Do(...)
        // }()
    })
  6. Intercept rueidis.Client with rueidishook

    main

    You can intercept rueidis.Client calls by implementing the rueidishook.Hook interface and using rueidishook.WithHook. This is useful for adding observability, APM, or modifying client behavior.

    To use it, implement the following methods in your custom hook struct:

    • Do(client rueidis.Client, ctx context.Context, cmd rueidis.Completed) (resp rueidis.RedisResult)
    • DoMulti(client rueidis.Client, ctx context.Context, multi ...rueidis.Completed) (resps []rueidis.RedisResult)
    • DoCache(client rueidis.Client, ctx context.Context, cmd rueidis.Cacheable, ttl time.Duration) (resp rueidis.RedisResult)
    • DoMultiCache(client rueidis.Client, ctx context.Context, multi ...rueidis.CacheableTTL) (resps []rueidis.RedisResult)
    • Receive(client rueidis.Client, ctx context.Context, subscribe rueidis.Completed, fn func(msg rueidis.PubSubMessage)) (err error)

    Wrap your existing client using rueidishook.WithHook(client, hookInstance).

    package main
    
    import (
    	"context"
    	"time"
    
    	"github.com/redis/rueidis"
    	"github.com/redis/rueidis/rueidishook"
    )
    
    type hook struct{}
    
    func (h *hook) Do(client rueidis.Client, ctx context.Context, cmd rueidis.Completed) (resp rueidis.RedisResult) {
    	// do whatever you want before a client.Do
    	resp = client.Do(ctx, cmd)
    	// do whatever you want after a client.Do
    	return
    }
    
    func (h *hook) DoMulti(client rueidis.Client, ctx context.Context, multi ...rueidis.Completed) (resps []rueidis.RedisResult) {
    	// do whatever you want before a client.DoMulti
    	resps = client.DoMulti(ctx, multi...)
    	// do whatever you want after a client.DoMulti
    	return
    }
    
    func (h *hook) DoCache(client rueidis.Client, ctx context.Context, cmd rueidis.Cacheable, ttl time.Duration) (resp rueidis.RedisResult) {
    	// do whatever you want before a client.DoCache
    	resp = client.DoCache(ctx, cmd, ttl)
    	// do whatever you want after a client.DoCache
    	return
    }
    
    func (h *hook) DoMultiCache(client rueidis.Client, ctx context.Context, multi ...rueidis.CacheableTTL) (resps []rueidis.RedisResult) {
    	// do whatever you want before a client.DoMultiCache
    	resps = client.DoMultiCache(ctx, multi...)
    	// do whatever you want after a client.DoMultiCache
    	return
    }
    
    func (h *hook) Receive(client rueidis.Client, ctx context.Context, subscribe rueidis.Completed, fn func(msg rueidis.PubSubMessage)) (err error) {
    	// do whatever you want before a client.Receive
    	err = client.Receive(ctx, subscribe, fn)
    	// do whatever you want after a client.Receive
    	return
    }
    
    func main() {
    	client, err := rueidis.NewClient(rueidis.ClientOption{InitAddress: []string{"127.0.0.1:6379"}})
    	if err != nil {
    		panic(err)
    	}
    	client = rueidishook.WithHook(client, &hook{})
    	defer client.Close()
    }
  7. Build Valkey with RDMA support

    main

    To build Valkey with RDMA module support, you must install the necessary build dependencies and RDMA development libraries, then set the BUILD_RDMA=module environment variable during the make process.

    sudo apt install -y build-essential pkg-config libjemalloc-dev
    sudo apt install -y librdmacm-dev rdma-core rdmacm-utils
    wget https://github.com/valkey-io/valkey/archive/refs/tags/9.0.1.tar.gz
    tar -zxvf 9.0.1.tar.gz && rm 9.0.1.tar.gz
    cd valkey-9.0.1
    
    BUILD_RDMA=module make
  8. Disable Client-Side Caching in rueidislock

    main

    If you are using a Redis provider that does not support client-side caching (such as Google Cloud Memorystore), you must disable it to ensure correct behavior.

    To disable client-side caching, set ClientOption.DisableCache to true within your LockerOption.

    Note: When client-side caching is disabled, rueidislock will only attempt to re-acquire locks at every ExtendInterval, rather than reacting immediately to notifications.

  9. Initialize and use rueidislock

    main

    rueidislock provides a distributed lock pattern enhanced by Redis Client-Side Caching. To use it, create a Locker using rueidislock.NewLocker and pass a LockerOption. You can then acquire a lock using WithContext, which returns a context that represents the lock's lifecycle. To release the lock, call the cancel function returned by WithContext.

    Key features include:

    • Automatic Cancellation: The returned ctx is canceled immediately if the KeyMajority is lost (e.g., Redis goes down or keys are deleted).
    • Automatic Retries: WithContext will automatically attempt to re-acquire the lock when it is released by another process, leveraging client-side caching notifications for immediate reaction.
    package main
    
    import (
    	"context"
    	"github.com/redis/rueidis"
    	"github.com/redis/rueidis/rueidislock
    )
    
    func main() {
    	locker, err := rueidislock.NewLocker(rueidislock.LockerOption{
    		ClientOption:   rueidis.ClientOption{InitAddress: []string{"localhost:6379"}},
    		KeyMajority:    1,    // Use KeyMajority=1 if you have only one Redis instance.
    		NoLoopTracking: true, // Enable for better performance if Redis >= 7.0.5.
    	})
    	if err != nil {
    		panic(err)
    	}
    	defer locker.Close()
    
    	// acquire the lock "my_lock"
    	ctx, cancel, err := locker.WithContext(context.Background(), "my_lock")
    	if err != nil {
    		panic(err)
    	}
    
    	// "my_lock" is acquired. use the ctx as normal.
    	doSomething(ctx)
    
    	// invoke cancel() to release the lock.
    	cancel()
    }
  10. Connect using Redis URLs

    main

    Use rueidis.ParseURL or rueidis.MustParseURL to construct a ClientOption from a connection string. Supported protocols are redis://, rediss://, and unix://.

    Supported URL parameters include:

    • db
    • dial_timeout
    • write_timeout
    • addr
    • protocol
    • client_cache
    • client_name
    • max_retries
    • master_set
    // connect to a redis cluster
    client, err = rueidis.NewClient(rueidis.MustParseURL("redis://127.0.0.1:7001?addr=127.0.0.1:7002&addr=127.0.0.1:7003"))
    // connect to a redis node
    client, err = rueidis.NewClient(rueidis.MustParseURL("redis://127.0.0.1:6379/0"))
    // connect to a redis sentinel
    client, err = rueidis.NewClient(rueidis.MustParseURL("redis://127.0.0.1:26379/0?master_set=my_master"))
    // connecting to redis node using unix socket
    client, err = rueidis.NewClient(rueidis.MustParseURL("unix:///run/redis.conf?db=0"))
  11. Use the rueidis Command Builder

    main

    The client.B() method provides an entry point to a developer-friendly command builder. Once a command is constructed, use client.Do() or client.DoMulti() to send it to Redis.

    Important: Command Recycling By default, commands are recycled to an underlying sync.Pool after execution. You MUST NOT reuse a command object in another client.Do() or client.DoMulti() call.

    To safely reuse a command, call .Pin() after .Build(). This prevents the command from being recycled.