Documentation
¶
Overview ¶
Package redis provides focused helpers built on go-redis for common application workflows: key/value storage, Pub/Sub messaging, typed payload encoding, and basic connection health checks.
Redis reference: https://redis.io Underlying client: https://github.com/redis/go-redis
Problem ¶
Direct use of go-redis is powerful but often leads to repeated adapter code in services: typed payload serialization, subscription channel wiring, and consistent error wrapping around set/get/publish operations. This package centralizes those patterns behind a minimal client API.
How It Works ¶
New creates a Client given a SrvOptions (aliased from go-redis Options) and a variadic list of Option values:
- The server address is validated before any network connection is attempted (skipped when a client is injected via WithRedisClient, since no connection is dialed). Both TCP host:port addresses and unix domain sockets are supported.
- A go-redis client is constructed (or injected via WithRedisClient for tests). When at least one channel is declared with WithChannels, a Pub/Sub subscription feeds Client.Receive and Client.ReceiveData one message per call. The subscription runs until Client.Close is called: canceling the New context does not stop it.
- Encode and decode functions — defaulting to DefaultMessageEncodeFunc and DefaultMessageDecodeFunc — are stored on the client and used transparently by the typed data methods.
Key Features ¶
- Raw operations: Client.Set, Client.Get, and Client.Del offer direct key/value access with expiration support.
- Typed data operations: Client.SetData and Client.GetData encode/decode Go values automatically using the configured TEncodeFunc / TDecodeFunc, removing serialization boilerplate at every call site.
- Pub/Sub: Client.Send and Client.Receive work with raw strings; Client.SendData and Client.ReceiveData apply the same encode/decode pipeline, returning the channel name alongside the decoded value.
- Pluggable serialization: WithMessageEncodeFunc and WithMessageDecodeFunc replace the default codec with any implementation — including encrypted or compressed payloads — without changing call sites.
- Health check: Client.HealthCheck sends a PING and returns a wrapped error on failure, making it trivial to integrate with liveness probes.
- Sentinel errors: a missing key surfaces as ErrKeyNotFound, and configuration or subscription states as the other exported Err values, all matchable with errors.Is.
- Mockable client: WithRedisClient injects a custom RClient, enabling fast, dependency-free unit tests of the command path.
- Graceful close: Client.Close releases Pub/Sub and client resources; it is idempotent and required to stop the subscription when channels are configured.
Subscription Configuration ¶
Use options to define Pub/Sub behavior at client creation time:
- WithChannels to subscribe to channels.
- WithChannelOptions to tune subscription channel behavior: buffer size, send timeout, and health check interval. Note that with the go-redis defaults, a consumer that stops calling Client.Receive loses messages once the 100-message buffer stays full for one minute.
Usage ¶
srv := &redis.SrvOptions{Addr: "localhost:6379"}
c, err := redis.New(ctx, srv, redis.WithChannels("events"))
if err != nil {
return err
}
defer c.Close()
if err := c.Set(ctx, "k", "v", 0); err != nil {
return err
}
if err := c.SendData(ctx, "events", event); err != nil {
return err
}
var event Event
channel, err := c.ReceiveData(ctx, &event)
if err != nil {
return err
}
if err := c.HealthCheck(ctx); err != nil {
return err
}
To swap in an encrypted codec, supply custom functions at construction time:
c, err := redis.New(ctx, srv,
redis.WithMessageEncodeFunc(myEncryptAndEncode),
redis.WithMessageDecodeFunc(myDecryptAndDecode),
)
Index ¶
- Variables
- func DefaultMessageDecodeFunc(_ context.Context, msg string, data any) error
- func DefaultMessageEncodeFunc(_ context.Context, data any) (string, error)
- func MessageDecode(msg string, data any) error
- func MessageEncode(data any) (string, error)
- type ChannelOption
- type Client
- func (c *Client) Close() error
- func (c *Client) Del(ctx context.Context, key string) error
- func (c *Client) Get(ctx context.Context, key string, value any) error
- func (c *Client) GetData(ctx context.Context, key string, data any) error
- func (c *Client) HealthCheck(ctx context.Context) error
- func (c *Client) Receive(ctx context.Context) (string, string, error)
- func (c *Client) ReceiveData(ctx context.Context, data any) (string, error)
- func (c *Client) Send(ctx context.Context, channel string, message any) error
- func (c *Client) SendData(ctx context.Context, channel string, data any) error
- func (c *Client) Set(ctx context.Context, key string, value any, exp time.Duration) error
- func (c *Client) SetData(ctx context.Context, key string, data any, exp time.Duration) error
- type Option
- type RClient
- type RMessage
- type RPubSub
- type SrvOptions
- type TDecodeFunc
- type TEncodeFunc
Constants ¶
This section is empty.
Variables ¶
var ( // ErrInvalidOptions is returned by New when no client is injected and the // server options carry a missing or malformed address. ErrInvalidOptions = errors.New("redis: missing or invalid client options") // ErrNilEncodeFunc is returned by New when the message encode function is nil. ErrNilEncodeFunc = errors.New("redis: nil message encode function") // ErrNilDecodeFunc is returned by New when the message decode function is nil. ErrNilDecodeFunc = errors.New("redis: nil message decode function") // ErrInvalidChannelName is returned by New when a subscription channel // name configured via WithChannels is empty. ErrInvalidChannelName = errors.New("redis: empty subscription channel name") // ErrKeyNotFound is returned by Get and GetData when the key does not // exist in the datastore. It signals a missing lookup target (e.g. maps // to HTTP 404) rather than a connection or protocol failure. ErrKeyNotFound = errors.New("redis: key not found") // ErrNoSubscription is returned by Receive and ReceiveData when the client // was constructed without any subscription channel (see WithChannels). ErrNoSubscription = errors.New("redis: no subscription channel configured") // ErrSubscriptionClosed is returned by Receive and ReceiveData after the // subscription message channel has been closed (e.g. on Close). ErrSubscriptionClosed = errors.New("redis: subscription closed") )
Sentinel errors returned by the package. They can be matched with errors.Is so callers can distinguish configuration problems from lookup and subscription states.
Functions ¶
func DefaultMessageDecodeFunc ¶
DefaultMessageDecodeFunc provides default decoding used by ReceiveData.
func DefaultMessageEncodeFunc ¶
DefaultMessageEncodeFunc provides default encoding used by SendData.
func MessageDecode ¶
MessageDecode decodes a MessageEncode payload into data, which must be a pointer.
func MessageEncode ¶
MessageEncode encodes and serializes data into a string payload.
Types ¶
type ChannelOption ¶
type ChannelOption = libredis.ChannelOption
ChannelOption aliases go-redis Pub/Sub channel options.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client wraps Redis KV/PubSub operations with optional typed payload codecs.
func New ¶
New constructs a Redis client wrapper with optional Pub/Sub subscriptions and pluggable message codecs.
A Pub/Sub subscription is established only when at least one channel is configured via WithChannels; otherwise no subscription resources are allocated.
ctx does not bound the lifetime of the client or the subscription: go-redis uses it only for the initial SUBSCRIBE command (whose failure surfaces through the subscription retry loop, not here) while the goroutine delivering messages to Receive runs until Close is called. Consequently, when channels are configured, Close is the only way to stop the subscription and must be called.
func (*Client) Close ¶
Close gracefully closes Pub/Sub and Redis client resources.
When no Pub/Sub subscription was configured at construction time, only the Redis client is closed. The Redis client is always closed even when closing the Pub/Sub subscription fails; in that case the errors are joined.
Close is idempotent: only the first call releases the resources, and every subsequent call returns the result of the first. When channels are configured, Close must be called to stop the background subscription: canceling the context passed to New does not.
func (*Client) Get ¶
Get retrieves the raw value of key and scans it into value.
value must be a pointer to a type supported by go-redis scanning: a string, a numeric or bool type, time.Time, time.Duration, []byte, or a type implementing encoding.BinaryUnmarshaler. Use GetData for values stored with SetData.
When the key does not exist, the returned error satisfies errors.Is(err, ErrKeyNotFound).
func (*Client) GetData ¶
GetData retrieves an encoded value from key and decodes it into data.
When the key does not exist, the returned error satisfies errors.Is(err, ErrKeyNotFound).
func (*Client) HealthCheck ¶
HealthCheck verifies Redis connectivity with a PING command.
func (*Client) Receive ¶
Receive returns the next raw message from subscribed channels as channel name and payload.
Each call consumes exactly one message delivered by the subscription established at construction time. It returns ErrNoSubscription when the client was constructed without any subscription channel (see WithChannels), a wrapped ctx.Err() when ctx is canceled or its deadline expires, and ErrSubscriptionClosed after Close. Match the sentinel errors with errors.Is. After Close, any messages already buffered are still delivered before ErrSubscriptionClosed is returned.
Messages are buffered in a Go channel holding 100 messages by default; when the buffer stays full for one minute (the go-redis default send timeout), the incoming message is silently dropped and only logged by go-redis. Slow consumers should tune this via WithChannelOptions (libredis.WithChannelSize, libredis.WithChannelSendTimeout).
func (*Client) ReceiveData ¶
ReceiveData receives an encoded message, decodes it into data, and returns the source channel. On any error, including a decode failure, the returned channel name is empty.
func (*Client) Send ¶
Send publishes a raw value to channel.
message must be a type supported by the go-redis protocol writer (see Set); use SendData for arbitrary values.
func (*Client) Set ¶
Set stores a raw value for key with expiration.
value must be a type supported by the go-redis protocol writer: a string, []byte, a numeric or bool type (or a pointer to one), time.Time, time.Duration, or a type implementing encoding.BinaryMarshaler; anything else fails at command time. Use SetData for arbitrary values.
A zero or negative exp (other than libredis.KeepTTL, which retains the key's existing TTL) stores the key without expiration. Positive durations below one millisecond are truncated to 1ms by go-redis.
type Option ¶
type Option func(*cfg)
Option customizes client configuration.
func WithChannelOptions ¶
func WithChannelOptions(opts ...ChannelOption) Option
WithChannelOptions sets subscription channel options for go-redis Pub/Sub (e.g. libredis.WithChannelSize, libredis.WithChannelSendTimeout, libredis.WithChannelHealthCheckInterval). It has no effect unless WithChannels configures at least one channel.
func WithChannels ¶
WithChannels sets Pub/Sub channels subscribed at client creation time.
func WithMessageDecodeFunc ¶
func WithMessageDecodeFunc(f TDecodeFunc) Option
WithMessageDecodeFunc overrides the decoder used by ReceiveData and GetData.
func WithMessageEncodeFunc ¶
func WithMessageEncodeFunc(f TEncodeFunc) Option
WithMessageEncodeFunc overrides the encoder used by SendData and SetData.
func WithRedisClient ¶
WithRedisClient injects an existing go-redis client, primarily for testing.
When a client is injected, the server options passed to New are never consulted and their address is not validated. Injection is intended for command-path testing: Get, Set, Del, Publish, and Ping results can be built with the public go-redis constructors (libredis.NewStringResult and friends). Combining an injected client with WithChannels requires its Subscribe to return a functional *libredis.PubSub, so subscription flows are better tested against a real server.
type RClient ¶
type RClient interface {
Close() error
Del(ctx context.Context, keys ...string) *libredis.IntCmd
Get(ctx context.Context, key string) *libredis.StringCmd
// Ping is used by HealthCheck.
Ping(ctx context.Context) *libredis.StatusCmd
Publish(ctx context.Context, channel string, message any) *libredis.IntCmd
Set(ctx context.Context, key string, value any, expiration time.Duration) *libredis.StatusCmd
Subscribe(ctx context.Context, channels ...string) *libredis.PubSub
}
RClient defines the go-redis client calls used by Client.
type RPubSub ¶
type RPubSub interface {
Channel(opts ...libredis.ChannelOption) <-chan *libredis.Message
Close() error
}
RPubSub defines the go-redis Pub/Sub calls used by Client.
type SrvOptions ¶
SrvOptions aliases go-redis client options used when constructing a client.
type TDecodeFunc ¶
TDecodeFunc is the type of function used to replace the default message decoding function used by ReceiveData() and GetData().