Documentation
¶
Overview ¶
Package redis wraps github.com/redis/go-redis/v9 with resilient defaults so a service gets a correctly configured Redis client from a single Connect call: a sensible pool, bounded timeouts, a few retries with backoff, and a startup Ping that verifies readiness. Functional options select a standalone server, a sentinel-managed failover master, or a cluster, and tune pool, timeouts, retries, TLS, and pluggable Logger/Observer hooks. The returned Client exposes the underlying go-redis UniversalClient for the full command API.
Client.FastFail returns a view of the same server for health probes and fail-open paths: no command retries, a single dial attempt and a short budget (WithFastFail), so a call fails in milliseconds while the server is down instead of paying the default retries. WithDialRetry makes the go-redis dial-retry defaults explicit and tunable.
Nil, Cmdable, and UniversalClient re-export the handful of go-redis names a caller needs to work with Client.Redis's return value -- the miss sentinel and the command/client interfaces -- so a consumer of this package never needs to import github.com/redis/go-redis/v9 directly.
Index ¶
- Variables
- func Transient(err error) bool
- func WaitFor(ctx context.Context, fn func(context.Context) error, opts ...WaitOption) error
- type Client
- type Cmdable
- type Cmder
- type Logger
- type Observer
- type Option
- func WithAddr(addrs ...string) Option
- func WithCluster(addrs ...string) Option
- func WithDB(db int) Option
- func WithDialRetry(attempts int, backoff time.Duration) Option
- func WithFastFail(timeout time.Duration) Option
- func WithLogger(l Logger) Option
- func WithObserver(o Observer) Option
- func WithPassword(password string) Option
- func WithPool(size int) Option
- func WithRetry(attempts int, min, max time.Duration) Option
- func WithSentinel(masterName string, addrs ...string) Option
- func WithTLS(cfg *tls.Config) Option
- func WithTimeouts(dial, read, write time.Duration) Option
- func WithUsername(username string) Option
- type Pipeliner
- type StringCmd
- type Tx
- type UniversalClient
- type WaitOption
- type XAddArgs
- type XMessage
- type XMessageSliceCmd
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var Nil = goredis.Nil
Nil is the sentinel error go-redis returns when a command finds no result (for example GET on a missing key). It is re-exported here so a caller can write errors.Is(err, redis.Nil) without importing github.com/redis/go-redis/v9 directly.
var TxFailedErr = goredis.TxFailedErr
TxFailedErr is the error a WATCH transaction returns when a watched key changed before EXEC, so nothing was written and the caller may retry. It is re-exported so a caller can write errors.Is(err, redis.TxFailedErr) without importing github.com/redis/go-redis/v9 directly.
Functions ¶
func Transient ¶ added in v1.3.0
Transient reports whether err means the server is not reachable or not ready yet, as opposed to a failure that retrying will not fix. It is true for network failures (refused or reset connections, failed DNS lookups, timeouts, a cut connection) and for the LOADING, MASTERDOWN, TRYAGAIN and CLUSTERDOWN replies. Any other reply, such as WRONGPASS or NOAUTH, is not transient, and neither is a closed client. It unwraps the chain, so the errors Connect returns qualify.
func WaitFor ¶ added in v1.3.0
WaitFor calls fn until it succeeds, retrying while its error is Transient, so a service can start before its Redis or Valkey answers instead of exiting. A non-transient error is returned at once. When ctx ends first, the error wraps both ctx's error and the last attempt's. Each wait is a capped exponential backoff with jitter. fn is typically a Connect call.
Types ¶
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is a thin, resilient wrapper over a go-redis UniversalClient. Build one with Connect; use Redis to reach the full go-redis command API, Healthy for a liveness probe, and Close to release the pool. A Client is safe for concurrent use by multiple goroutines.
func Connect ¶
Connect builds a Redis client from the given options, applies the resilient defaults for anything left unset, and verifies readiness with a Ping before returning. The mode (standalone, sentinel, or cluster) is chosen by the WithAddr, WithSentinel, or WithCluster option; standalone localhost:6379 is the default. On a failed Ping the underlying client is closed and the error is returned, so a returned Client is always usable. The ctx bounds both the dial and the readiness Ping.
Example ¶
Connect to a standalone server with the resilient defaults, then read and write through the underlying go-redis client.
package main
import (
"context"
"fmt"
"log"
"time"
redis "github.com/Bugs5382/go-redis"
)
func main() {
ctx := context.Background()
client, err := redis.Connect(ctx, redis.WithAddr("localhost:6379"))
if err != nil {
log.Fatal(err)
}
defer func() { _ = client.Close() }()
rdb := client.Redis()
if err := rdb.Set(ctx, "session:123", "alice", time.Minute).Err(); err != nil {
log.Fatal(err)
}
name, err := rdb.Get(ctx, "session:123").Result()
if err != nil {
log.Fatal(err)
}
fmt.Println(name)
}
Output:
Example (Sentinel) ¶
Connect through Redis Sentinel for high availability: the client discovers the current master through the sentinels and re-resolves it after a failover.
package main
import (
"context"
"fmt"
"log"
"time"
redis "github.com/Bugs5382/go-redis"
)
func main() {
ctx := context.Background()
client, err := redis.Connect(ctx,
redis.WithSentinel("mymaster", "localhost:26379", "localhost:26380"),
redis.WithPassword("s3cret"),
redis.WithPool(50),
redis.WithTimeouts(5*time.Second, 3*time.Second, 3*time.Second),
redis.WithRetry(3, 8*time.Millisecond, 512*time.Millisecond),
)
if err != nil {
log.Fatal(err)
}
defer func() { _ = client.Close() }()
if client.Healthy(ctx) {
fmt.Println("ready")
}
}
Output:
func (*Client) Close ¶
Close releases the connection pool, and the FastFail view's pool when one was built. It is safe to call once; further use of the Client after Close returns errors from go-redis. On the FastFail view, Close does nothing: the parent owns the view's pool.
func (*Client) FastFail ¶ added in v1.4.0
FastFail returns a view of the same server that gives up quickly instead of retrying: no command retries, a single dial attempt, and the WithFastFail budget (default 200ms) as its dial, read, write and pool-wait timeouts. While the server is down, a call through the view fails in about the time it takes to be refused (or the budget, for a host that does not answer) instead of the seconds the retrying path can spend.
Use it where Redis is optional and the caller has a fallback: a cache read that falls back to the source, a rate-limit check that lets the request through, a readiness probe. The returned value is a *Client, so it plugs into the toolkit packages (cache, ratelimit and the rest) unchanged. Errors from the view are the usual go-redis errors; Transient reports true for the ones that mean the server is unreachable.
The view has its own small connection pool, built on the first call and shared by every later call; it opens connections only when used. It inherits the Logger and Observer given to Connect. The parent owns it: Close on the view is a no-op, and closing the parent closes the view. FastFail on the view returns the view.
Example ¶
Name the command surface with this package's own alias -- Cmdable -- and check a miss against the re-exported Nil sentinel. Neither line requires importing github.com/redis/go-redis/v9. Read through the fail-fast view and fall back when Redis is unavailable, instead of waiting out the default retries.
package main
import (
"context"
"errors"
"fmt"
"log"
"time"
redis "github.com/Bugs5382/go-redis"
)
func main() {
ctx := context.Background()
client, err := redis.Connect(ctx,
redis.WithAddr("localhost:6379"),
redis.WithFastFail(100*time.Millisecond),
)
if err != nil {
log.Fatal(err)
}
defer func() { _ = client.Close() }()
ff := client.FastFail()
if !ff.Healthy(ctx) {
fmt.Println("not ready")
}
flag, err := ff.Redis().Get(ctx, "flags:new-checkout").Result()
switch {
case err == nil:
fmt.Println("flag:", flag)
case errors.Is(err, redis.Nil):
fmt.Println("flag not set")
default:
fmt.Println("flag store unavailable, using the default")
}
}
Output:
func (*Client) Healthy ¶
Healthy reports whether the server answers a Ping within ctx. On a Client from Connect the Ping carries the normal retries, so while the server is down it can take a second or more to say false; for a readiness probe use c.FastFail().Healthy(ctx), which answers within the fast-fail budget.
func (*Client) Redis ¶
func (c *Client) Redis() UniversalClient
Redis returns the underlying client as the UniversalClient alias so callers can use the full command API (Get, Set, pipelines, pub/sub, scripts, and so on) while naming only this package's exported types. The returned value is owned by this Client; do not Close it directly -- use Client.Close.
Example ¶
package main
import (
"context"
"errors"
"fmt"
"log"
redis "github.com/Bugs5382/go-redis"
)
func main() {
ctx := context.Background()
client, err := redis.Connect(ctx, redis.WithAddr("localhost:6379"))
if err != nil {
log.Fatal(err)
}
defer func() { _ = client.Close() }()
var cmd redis.Cmdable = client.Redis()
_, err = cmd.Get(ctx, "session:missing").Result()
if errors.Is(err, redis.Nil) {
fmt.Println("cache miss")
}
}
Output:
type Cmdable ¶ added in v1.1.0
Cmdable is the full go-redis command surface (Get, Set, pipelines, pub/sub, scripts, and so on), re-exported as a type alias so a caller can name it -- for example to store the result of Client.Redis in a typed field or accept it as a function parameter -- without importing github.com/redis/go-redis/v9 directly.
type Cmder ¶ added in v1.5.0
Cmder is any queued or executed command, as Pipelined and TxPipelined return them. Re-exported as a type alias.
type Logger ¶
type Logger interface {
// Printf logs a formatted message. ctx carries the call's context, which
// may hold trace information.
Printf(ctx context.Context, format string, args ...any)
}
Logger receives connection-level diagnostic messages. Implementations must be safe for concurrent use. The default is a no-op, so the core carries no logging dependency.
type Observer ¶
type Observer interface {
// ObserveCommand is called after each command completes. name is the Redis
// command name, dur is how long it took, and err is its result (nil on
// success; note goredis.Nil signals a missing key, not a failure).
ObserveCommand(ctx context.Context, name string, dur time.Duration, err error)
// ObserveDial is called after each attempt to open a new connection.
ObserveDial(ctx context.Context, network, addr string, dur time.Duration, err error)
}
Observer receives lightweight per-command and per-dial signals, suitable for driving metrics without pulling a telemetry library into the core. Implementations must be safe for concurrent use and must not block. The default is a no-op. For full OpenTelemetry tracing and metrics, use the otel subpackage instead.
type Option ¶
type Option func(*config)
Option configures a Client. Options are applied in order by Connect; a later Option wins over an earlier one.
func WithAddr ¶
WithAddr targets a standalone Redis server. The first address is used; extra addresses are ignored (use WithCluster for multiple nodes). Overrides the default localhost:6379.
func WithCluster ¶
WithCluster targets a Redis Cluster. addrs are seed node endpoints; the client discovers the rest of the topology and reshards transparently. Note: cluster mode ignores WithDB (Redis Cluster supports only database 0).
func WithDialRetry ¶ added in v1.4.0
WithDialRetry tunes how hard each new connection is dialed: attempts is the total number of dial tries (1 means a single try with no retry) and backoff is the pause between them. The defaults, 5 attempts and 100ms, are the go-redis values; together with WithRetry they decide how long a call takes to fail while the server is down (at least (attempts-1) x backoff per dial). Lowering them makes every call fail sooner but gives up on a server that is only restarting; for a per-call choice, use Client.FastFail instead. Non-positive values keep the defaults.
func WithFastFail ¶ added in v1.4.0
WithFastFail sets the per-operation budget of the Client.FastFail view: its dial, read, write and pool-wait timeouts (default 200ms). Pick a value above the server's normal worst-case latency; a call that needs longer fails on the view and should take the caller's fallback. A non-positive value keeps the default.
func WithLogger ¶
WithLogger installs a Logger for connection-level diagnostics (dial failures). The default is a no-op; the core imposes no logging dependency.
func WithObserver ¶
WithObserver installs an Observer for lightweight command and dial metrics. The default is a no-op; the core imposes no telemetry dependency.
func WithPassword ¶
WithPassword sets the AUTH password. Leave unset for an unauthenticated server.
func WithPool ¶
WithPool sets the maximum number of socket connections in the pool. A non-positive size is ignored, keeping the resilient default.
func WithRetry ¶
WithRetry tunes command retries: attempts is the maximum number of retries after the first try (mapped to go-redis MaxRetries), and min/max bound the exponential backoff between them. A negative attempts count is ignored; pass 0 to disable retries. Non-positive backoff bounds are left at their defaults.
func WithSentinel ¶
WithSentinel targets a Redis Sentinel deployment: masterName is the monitored master's name (commonly "mymaster") and addrs are the sentinel endpoints. This is the high-availability path -- go-redis discovers the current master through the sentinels and re-resolves it after a failover.
func WithTLS ¶
WithTLS enables TLS using the given configuration. Pass a non-nil *tls.Config (an empty &tls.Config{} uses the system roots and the server's hostname). A nil config leaves TLS disabled.
func WithTimeouts ¶
WithTimeouts overrides the dial, read, and write timeouts. A non-positive duration for any parameter leaves that timeout at its default.
func WithUsername ¶ added in v1.5.0
WithUsername sets the ACL user (Redis 6+, Valkey) that authenticates with the WithPassword password, so a service can run under its own least-privilege account instead of the server's default user. It applies to standalone, cluster and sentinel connections (the master and replicas the sentinels point to; the sentinels themselves are not authenticated). Leave it unset, or pass "", to AUTH with the password alone as the default user.
Example ¶
Connect as a named ACL user instead of the server's default user.
package main
import (
"context"
"log"
redis "github.com/Bugs5382/go-redis"
)
func main() {
ctx := context.Background()
client, err := redis.Connect(ctx,
redis.WithAddr("cache.example.org:6379"),
redis.WithUsername("orders-svc"),
redis.WithPassword("s3cret"),
)
if err != nil {
log.Fatal(err)
}
defer func() { _ = client.Close() }()
}
Output:
type Pipeliner ¶ added in v1.5.0
Pipeliner is the queue a Pipelined or TxPipelined callback adds commands to. Re-exported as a type alias so the callback can be written without the upstream import.
type StringCmd ¶ added in v1.5.0
StringCmd is the result of a command that replies with one string (GET and friends), for holding a pipelined result until the pipeline runs. Re-exported as a type alias.
type Tx ¶ added in v1.5.0
Tx is the connection UniversalClient.Watch hands its callback: commands on it read the watched keys, and its TxPipelined queues the MULTI/EXEC writes. Re-exported as a type alias so the callback can be written without the upstream import.
Example ¶
An optimistic WATCH transaction that appends to a stream, written with this package's aliases only.
package main
import (
"context"
"errors"
"fmt"
"log"
redis "github.com/Bugs5382/go-redis"
)
func main() {
ctx := context.Background()
client, err := redis.Connect(ctx, redis.WithAddr("localhost:6379"))
if err != nil {
log.Fatal(err)
}
defer func() { _ = client.Close() }()
err = client.Redis().Watch(ctx, func(tx *redis.Tx) error {
n, err := tx.Get(ctx, "audit:head").Result()
if err != nil && !errors.Is(err, redis.Nil) {
return err
}
_, err = tx.TxPipelined(ctx, func(p redis.Pipeliner) error {
p.XAdd(ctx, &redis.XAddArgs{Stream: "audit", ID: "*", Values: map[string]any{"prev": n}})
p.Set(ctx, "audit:head", "next", 0)
return nil
})
return err
}, "audit:head")
if errors.Is(err, redis.TxFailedErr) {
fmt.Println("another writer got there first: retry")
}
}
Output:
type UniversalClient ¶ added in v1.1.0
type UniversalClient = goredis.UniversalClient
UniversalClient is the client-mode-agnostic go-redis interface (standalone, sentinel-failover, or cluster) that Client wraps, re-exported as a type alias for the same reason as Cmdable. It embeds Cmdable and additionally exposes hooks, transactions, pub/sub, and pool stats.
type WaitOption ¶ added in v1.3.0
type WaitOption func(*waitConfig)
WaitOption configures WaitFor.
func WithRetryHook ¶ added in v1.3.0
func WithRetryHook(hook func(attempt int, err error, delay time.Duration)) WaitOption
WithRetryHook calls hook after each transient failure, before the wait: attempt is 1-based, err is the failure and delay the wait that follows. Use it to log one line per attempt.
func WithWaitBackoff ¶ added in v1.3.0
func WithWaitBackoff(base, max time.Duration) WaitOption
WithWaitBackoff sets the delay before the first retry (base) and its cap (max); each wait doubles up to max, with jitter. Non-positive values keep the defaults (500ms and 15s).
type XAddArgs ¶ added in v1.5.0
XAddArgs are the arguments to XADD (the stream, the entry ID or "*", the field values and the optional trimming). Re-exported as a type alias.
type XMessage ¶ added in v1.5.0
XMessage is one stream entry, its ID and field values, as XRANGE and XREAD return them. Re-exported as a type alias.
type XMessageSliceCmd ¶ added in v1.5.0
type XMessageSliceCmd = goredis.XMessageSliceCmd
XMessageSliceCmd is the result of XRANGE and XREVRANGE, for holding a pipelined result until the pipeline runs. Re-exported as a type alias.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package cache adds typed get-or-set caching on top of a go-redis Client, with pluggable codecs, key namespacing, TTL jitter, negative caching and stampede protection.
|
Package cache adds typed get-or-set caching on top of a go-redis Client, with pluggable codecs, key namespacing, TTL jitter, negative caching and stampede protection. |
|
Package idempotency runs a function at most once per key within a TTL and replays its result, built on a go-redis Client and the script package.
|
Package idempotency runs a function at most once per key within a TTL and replays its result, built on a go-redis Client and the script package. |
|
Package lock is a Redis lock with safe release and optional auto-extend, built on a go-redis Client and the script package.
|
Package lock is a Redis lock with safe release and optional auto-extend, built on a go-redis Client and the script package. |
|
Package otel adds optional OpenTelemetry tracing and metrics to a go-redis Client in one call.
|
Package otel adds optional OpenTelemetry tracing and metrics to a go-redis Client in one call. |
|
Package pubsub is a Redis pub/sub subscriber that re-subscribes to its channels and patterns after a reconnect or failover, with its health visible to readiness probes.
|
Package pubsub is a Redis pub/sub subscriber that re-subscribes to its channels and patterns after a reconnect or failover, with its health visible to readiness probes. |
|
Package ratelimit is a distributed rate limiter backed by atomic Lua scripts, built on a go-redis Client and the script package.
|
Package ratelimit is a distributed rate limiter backed by atomic Lua scripts, built on a go-redis Client and the script package. |
|
Package script runs Lua scripts on a go-redis Client with EVALSHA, falling back to EVAL when the server answers NOSCRIPT.
|
Package script runs Lua scripts on a go-redis Client with EVALSHA, falling back to EVAL when the server answers NOSCRIPT. |
|
Package streams is a resilient Redis Streams consumer-group consumer, with a matching Publish, built on a go-redis Client.
|
Package streams is a resilient Redis Streams consumer-group consumer, with a matching Publish, built on a go-redis Client. |