redis

redis wraps go-redis for key/value storage, typed payload encoding and Pub/Sub, validating the address before dialling and exposing a health check.

Part of nurago, a collection of independent Go packages for backend services.

import "github.com/tecnickcom/nurago/pkg/redis"

Key/value operations, typed payloads and Pub/Sub over go-redis.

c, err := redis.New(ctx, &redis.SrvOptions{Addr: "localhost:6379"})
defer c.Close()

err = c.SetData(ctx, "key", value, time.Hour)
err = c.GetData(ctx, "key", &value)

Typed payloads

SetData and GetData serialise and deserialise around the raw string operations, so a struct goes in and comes back out without the call site handling encoding.

DefaultMessageEncodeFunc and DefaultMessageDecodeFunc are replaceable, which matters when the data has to be readable by something that is not this service. A shared cache populated by a Go service and read by a Python one needs an encoding both understand, and that choice belongs at construction rather than at each call.

The raw string operations remain available for values that are already bytes.

The address is validated before dialling

New validates the server address before attempting a connection, covering both TCP host:port and Unix domain sockets. Validation is skipped when a client is injected through WithRedisClient, since nothing is dialled.

A malformed address otherwise produces a connection error at first use, in a request, rather than at startup where a deployment can fail cleanly.

Pub/Sub outlives the New context

This is the behaviour most likely to surprise, and the doc comment is explicit about it.

When channels are declared with WithChannels, a subscription feeds Receive and ReceiveData one message per call. The subscription runs until Close is called. Cancelling the context passed to New does not stop it.

So a service that cancels its startup context after initialisation still has a live subscription, and one that relies on context cancellation for shutdown leaks the goroutine and the connection. Close is the only way to stop it, and it belongs in the shutdown path rather than in a defer on a startup function.

This mirrors the split in sqlconn: the constructor’s context bounds establishment, not the lifetime of the thing constructed.

One message per call

Receive returns a single message per invocation rather than handing back a channel.

That keeps back-pressure at the call site. A consumer loop processes one message and asks for the next when ready, so a slow consumer does not accumulate an unbounded in-memory backlog the way a buffered channel would.

Redis Pub/Sub is fire-and-forget: there is no acknowledgement, no replay, and a message published while no subscriber is connected is gone. For work that must not be lost, use kafka or sqs.

Health check and injection

Client.HealthCheck satisfies the healthcheck contract directly.

WithRedisClient injects a client, which is how tests substitute a mock or a miniredis instance without a server.

SrvOptions is an alias for go-redis Options, so pool sizing, TLS, credentials and timeouts are configured exactly as the underlying library documents, with no translation layer to learn.

Dependencies

Reaches github.com/redis/go-redis.