redis

package
v2.5.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 25, 2026 License: MIT Imports: 30 Imported by: 0

Documentation

Overview

Package redis provides Redis/Valkey client helpers with topology and IAM support.

Supported deployment modes include standalone, sentinel, and cluster. Authentication supports static passwords and short-lived GCP IAM tokens with automatic refresh and reconnect.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrNilLockHandle is returned when a nil or uninitialized lock handle is used.
	ErrNilLockHandle = errors.New("lock handle is nil or not initialized")
	// ErrLockNotHeld is returned when unlock is called on a lock that was not held or already expired.
	ErrLockNotHeld = errors.New("lock was not held or already expired")
	// ErrNilLockManager is returned when a method is called on a nil RedisLockManager.
	ErrNilLockManager = errors.New("lock manager is nil")
	// ErrLockNotInitialized is returned when the distributed lock's redsync is not initialized.
	ErrLockNotInitialized = errors.New("distributed lock is not initialized")
	// ErrNilLockFn is returned when a nil function is passed to WithLock.
	ErrNilLockFn = errors.New("lock function is nil")
	// ErrEmptyLockKey is returned when an empty lock key is provided.
	ErrEmptyLockKey = errors.New("lock key cannot be empty")
	// ErrLockExpiryInvalid is returned when lock expiry is not positive.
	ErrLockExpiryInvalid = errors.New("lock expiry must be greater than 0")
	// ErrLockTriesInvalid is returned when lock tries is less than 1.
	ErrLockTriesInvalid = errors.New("lock tries must be at least 1")
	// ErrLockTriesExceeded is returned when lock tries exceeds the maximum.
	ErrLockTriesExceeded = errors.New("lock tries exceeds maximum")
	// ErrLockRetryDelayNegative is returned when retry delay is negative.
	ErrLockRetryDelayNegative = errors.New("lock retry delay cannot be negative")
	// ErrLockDriftFactorInvalid is returned when drift factor is outside [0, 1).
	ErrLockDriftFactorInvalid = errors.New("lock drift factor must be between 0 (inclusive) and 1 (exclusive)")
	// ErrNilLockHandleOnUnlock is returned when Unlock is called with a nil handle.
	ErrNilLockHandleOnUnlock = errors.New("lock handle is nil")
)
View Source
var (
	// ErrNilClient is returned when a redis client receiver is nil.
	ErrNilClient = errors.New("redis client is nil")
	// ErrInvalidConfig indicates the provided redis configuration is invalid.
	ErrInvalidConfig = errors.New("invalid redis config")
)

Functions

func SetPackageLogger added in v2.3.0

func SetPackageLogger(l log.Logger)

SetPackageLogger configures a package-level logger used for nil-receiver assertion diagnostics and telemetry reporting. This is typically called once during application bootstrap. If l is nil, a NopLogger is used.

Types

type Auth

type Auth struct {
	StaticPassword *StaticPasswordAuth
	GCPIAM         *GCPIAMAuth
}

Auth selects one Redis authentication strategy.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client wraps a redis.UniversalClient with reconnection and IAM token refresh logic.

func New

func New(ctx context.Context, cfg Config) (*Client, error)

New validates config, connects to Redis, and returns a ready client.

func (*Client) Close

func (c *Client) Close() error

Close stops background refresh and closes the underlying Redis client.

func (*Client) Connect

func (c *Client) Connect(ctx context.Context) error

Connect establishes a Redis connection using the current client configuration.

func (*Client) GetClient

func (c *Client) GetClient(ctx context.Context) (redis.UniversalClient, error)

GetClient returns a connected redis client, reconnecting on demand if needed.

func (*Client) IsConnected

func (c *Client) IsConnected() (bool, error)

IsConnected reports whether the underlying client is currently connected.

func (*Client) LastRefreshError

func (c *Client) LastRefreshError() error

LastRefreshError returns the latest IAM refresh/reconnect error.

func (*Client) Status

func (c *Client) Status() (Status, error)

Status returns a snapshot of connectivity and token refresh state.

type ClusterTopology

type ClusterTopology struct {
	Addresses []string
}

ClusterTopology configures Redis cluster access.

type Config

type Config struct {
	Topology       Topology
	TLS            *TLSConfig
	Auth           Auth
	Options        ConnectionOptions
	Logger         log.Logger
	MetricsFactory *metrics.MetricsFactory
}

Config defines Redis client topology, auth, TLS, and connection settings.

type ConnectionOptions

type ConnectionOptions struct {
	DB              int
	Protocol        int
	PoolSize        int
	MinIdleConns    int
	ReadTimeout     time.Duration
	WriteTimeout    time.Duration
	DialTimeout     time.Duration
	PoolTimeout     time.Duration
	MaxRetries      int
	MinRetryBackoff time.Duration
	MaxRetryBackoff time.Duration
}

ConnectionOptions configures protocol, timeouts, pools, and retries.

type GCPIAMAuth

type GCPIAMAuth struct {
	CredentialsBase64       string
	ServiceAccount          string
	TokenLifetime           time.Duration
	RefreshEvery            time.Duration
	RefreshCheckInterval    time.Duration
	RefreshOperationTimeout time.Duration
}

GCPIAMAuth authenticates with short-lived GCP IAM access tokens.

func (GCPIAMAuth) GoString added in v2.3.0

func (a GCPIAMAuth) GoString() string

GoString returns a redacted representation for fmt %#v.

func (GCPIAMAuth) String added in v2.3.0

func (a GCPIAMAuth) String() string

String returns a redacted representation to prevent accidental credential logging.

type LockHandle added in v2.3.0

type LockHandle interface {
	// Unlock releases the distributed lock.
	Unlock(ctx context.Context) error
}

LockHandle represents an acquired distributed lock. It is obtained from TryLock and must be released via its Unlock method.

Example usage:

handle, acquired, err := locker.TryLock(ctx, "lock:resource:123")
if err != nil {
    return err
}
if !acquired {
    return nil // lock busy, skip
}
defer handle.Unlock(ctx)
// ... critical section ...

type LockManager added in v2.3.0

type LockManager interface {
	// WithLock executes a function while holding a distributed lock with default options.
	// The lock is automatically released when the function returns.
	WithLock(ctx context.Context, lockKey string, fn func(context.Context) error) error

	// WithLockOptions executes a function while holding a distributed lock with custom options.
	// Use this for fine-grained control over lock behavior.
	WithLockOptions(ctx context.Context, lockKey string, opts LockOptions, fn func(context.Context) error) error

	// TryLock attempts to acquire a lock without retrying.
	// Returns the handle and true if lock was acquired, nil and false otherwise.
	// Use LockHandle.Unlock to release the lock when done.
	TryLock(ctx context.Context, lockKey string) (LockHandle, bool, error)
}

LockManager provides an interface for distributed locking operations. This interface allows for easy mocking in tests without requiring a real Redis instance.

Example test implementation:

type MockLockManager struct{}

func (m *MockLockManager) WithLock(ctx context.Context, lockKey string, fn func(context.Context) error) error {
    // In tests, just execute the function without actual locking
    return fn(ctx)
}

func (m *MockLockManager) WithLockOptions(ctx context.Context, lockKey string, opts LockOptions, fn func(context.Context) error) error {
    return fn(ctx)
}

func (m *MockLockManager) TryLock(ctx context.Context, lockKey string) (LockHandle, bool, error) {
    return &mockHandle{}, true, nil
}

type LockOptions

type LockOptions struct {
	// Expiry is how long the lock is held before auto-expiring (prevents deadlocks)
	// Default: 10 seconds
	Expiry time.Duration

	// Tries is the number of attempts to acquire the lock before giving up
	// Default: 3, Maximum: 1000
	Tries int

	// RetryDelay is the delay between retry attempts
	// Default: 500ms
	RetryDelay time.Duration

	// DriftFactor accounts for clock drift in distributed systems (RedLock algorithm)
	// Default: 0.01 (1%)
	DriftFactor float64
}

LockOptions configures lock behavior for advanced use cases. Use DefaultLockOptions() for sensible defaults.

func DefaultLockOptions

func DefaultLockOptions() LockOptions

DefaultLockOptions returns production-ready defaults for distributed locking. These values are tuned for typical microservice scenarios with: - Operations completing within seconds - Network latency < 100ms - Acceptable retry overhead

func RateLimiterLockOptions

func RateLimiterLockOptions() LockOptions

RateLimiterLockOptions returns optimized defaults for rate limiter locking. These values are tuned for short, fast operations like rate limiting: - Quick operations (< 100ms) - Fast retry for better throughput - Lower expiry to reduce contention

type RedisLockManager added in v2.3.0

type RedisLockManager struct {
	// contains filtered or unexported fields
}

RedisLockManager provides distributed locking capabilities using Redis and the RedLock algorithm. This implementation ensures mutual exclusion across multiple service instances, preventing race conditions in critical sections such as: - Password update operations - Cache invalidation - Rate limiting checks - Any other operation requiring distributed coordination

The RedLock algorithm provides strong guarantees even in the presence of: - Network partitions - Process crashes - Clock drift

Example usage:

lock, err := redis.NewRedisLockManager(redisClient)
if err != nil {
    return err
}

err = lock.WithLock(ctx, "lock:user:123", func(ctx context.Context) error {
    // Critical section - only one instance will execute this at a time
    return updateUser(123)
})

func NewRedisLockManager added in v2.3.0

func NewRedisLockManager(conn *Client) (*RedisLockManager, error)

NewRedisLockManager creates a new distributed lock manager. The lock manager uses the RedLock algorithm for distributed consensus. It uses a lazy pool that resolves the latest Redis client per operation, surviving IAM token refresh reconnections.

Thread-safe: Yes - multiple goroutines can use the same RedisLockManager instance.

Example:

lock, err := redis.NewRedisLockManager(redisClient)
if err != nil {
    return fmt.Errorf("failed to initialize lock: %w", err)
}

func (*RedisLockManager) TryLock added in v2.3.0

func (dl *RedisLockManager) TryLock(ctx context.Context, lockKey string) (LockHandle, bool, error)

TryLock attempts to acquire a lock without retrying. Returns the handle and true if lock was acquired, nil and false if lock is busy. Returns an error for unexpected failures (network errors, context cancellation, etc.)

Use LockHandle.Unlock to release the lock when done:

handle, acquired, err := lock.TryLock(ctx, "lock:cache:refresh")
if err != nil {
    // Unexpected error (network, context cancellation, etc.) - should be propagated
    return fmt.Errorf("failed to attempt lock acquisition: %w", err)
}
if !acquired {
    logger.Info("Lock busy, skipping cache refresh")
    return nil
}
defer handle.Unlock(ctx)
// Perform cache refresh...

func (*RedisLockManager) Unlock deprecated added in v2.3.0

func (dl *RedisLockManager) Unlock(ctx context.Context, handle LockHandle) error

Unlock releases a previously acquired lock.

Deprecated: Use LockHandle.Unlock() directly instead. This method is provided for backward compatibility during migration from the old *redsync.Mutex-based API.

func (*RedisLockManager) WithLock added in v2.3.0

func (dl *RedisLockManager) WithLock(ctx context.Context, lockKey string, fn func(context.Context) error) error

WithLock executes a function while holding a distributed lock. The lock is automatically released when the function returns, even on panic.

Parameters:

  • ctx: context for cancellation and tracing
  • lockKey: unique identifier for the lock (e.g., "lock:user:123")
  • fn: function to execute under lock

Returns:

  • error: from fn() or lock acquisition failure

Example:

err := lock.WithLock(ctx, "lock:user:password:123", func(ctx context.Context) error {
    return updatePassword(123, newPassword)
})

func (*RedisLockManager) WithLockOptions added in v2.3.0

func (dl *RedisLockManager) WithLockOptions(ctx context.Context, lockKey string, opts LockOptions, fn func(context.Context) error) error

WithLockOptions executes a function while holding a distributed lock with custom options. Use this when you need fine-grained control over lock behavior.

Example with custom timeout:

opts := redis.LockOptions{
    Expiry:     30 * time.Second, // Long-running operation
    Tries:      5,                 // More aggressive retries
    RetryDelay: 1 * time.Second,
}
err := lock.WithLockOptions(ctx, "lock:report:generation", opts, func(ctx context.Context) error {
    return generateReport()
})

type SentinelTopology

type SentinelTopology struct {
	Addresses  []string
	MasterName string
}

SentinelTopology configures Redis Sentinel access.

type StandaloneTopology

type StandaloneTopology struct {
	Address string
}

StandaloneTopology configures single-node Redis access.

type StaticPasswordAuth

type StaticPasswordAuth struct {
	Password string
}

StaticPasswordAuth authenticates using a static password.

func (StaticPasswordAuth) GoString added in v2.3.0

func (a StaticPasswordAuth) GoString() string

GoString returns a redacted representation for fmt %#v.

func (StaticPasswordAuth) String added in v2.3.0

func (StaticPasswordAuth) String() string

String returns a redacted representation to prevent accidental credential logging.

type Status

type Status struct {
	Connected          bool
	LastRefreshError   error
	LastRefreshAt      time.Time
	RefreshLoopRunning bool
}

Status reports client connectivity and IAM refresh loop health.

type TLSConfig

type TLSConfig struct {
	CACertBase64          string
	MinVersion            uint16
	AllowLegacyMinVersion bool
}

TLSConfig configures TLS validation for Redis connections.

type Topology

type Topology struct {
	Standalone *StandaloneTopology
	Sentinel   *SentinelTopology
	Cluster    *ClusterTopology
}

Topology selects exactly one Redis deployment mode.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL