Install rueidislimiter
mainTo install the rueidislimiter module, use the following Go command:
go get github.com/redis/rueidis/rueidislimiterrepository·main·Indexed 25 days ago
https://github.com/redis/rueidisA 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.
To install the rueidislimiter module, use the following Go command:
go get github.com/redis/rueidis/rueidislimiterAfter 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 6378The 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")
}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"}},
})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 attemptsrueidis_dial_success: number of successful dialsrueidis_dial_conns: number of connectionsrueidis_dial_latency: dial latency in secondsClient-side Caching Metrics:
rueidis_do_cache_miss: number of cache misses on client siderueidis_do_cache_hits: number of cache hits on client sideClient-side Command Metrics:
rueidis_command_duration_seconds: histogram of command durationrueidis_command_errors: number of command errorsNote: 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)
}To receive messages from channels (supporting SUBSCRIBE, PSUBSCRIBE, and Redis 7.0's SSUBSCRIBE), use client.Receive().
Best Practices:
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:
rueidis.ErrClosing).ctx.Err()).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(...)
// }()
})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()
}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 makeIf 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.
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:
ctx is canceled immediately if the KeyMajority is lost (e.g., Redis goes down or keys are deleted).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()
}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:
dbdial_timeoutwrite_timeoutaddrprotocolclient_cacheclient_namemax_retriesmaster_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"))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.