You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
199 lines
4.9 KiB
Go
199 lines
4.9 KiB
Go
// Package redis provides Redis client for the KnowFoolery application.
|
|
package redis
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"strconv"
|
|
"time"
|
|
|
|
redisv9 "github.com/redis/go-redis/v9"
|
|
)
|
|
|
|
// Config holds the configuration for the Redis client.
|
|
type Config struct {
|
|
Host string
|
|
Port int
|
|
Password string
|
|
DB int
|
|
PoolSize int
|
|
MinIdleConns int
|
|
DialTimeout time.Duration
|
|
ReadTimeout time.Duration
|
|
WriteTimeout time.Duration
|
|
}
|
|
|
|
// DefaultConfig returns a default configuration for development.
|
|
func DefaultConfig() Config {
|
|
return Config{
|
|
Host: "localhost",
|
|
Port: 6379,
|
|
Password: "",
|
|
DB: 0,
|
|
PoolSize: 10,
|
|
MinIdleConns: 5,
|
|
DialTimeout: 5 * time.Second,
|
|
ReadTimeout: 3 * time.Second,
|
|
WriteTimeout: 3 * time.Second,
|
|
}
|
|
}
|
|
|
|
// Addr returns the Redis server address.
|
|
func (c Config) Addr() string {
|
|
return fmt.Sprintf("%s:%d", c.Host, c.Port)
|
|
}
|
|
|
|
// Client wraps Redis operations.
|
|
// This is a placeholder that should be replaced with an actual Redis client.
|
|
type Client struct {
|
|
config Config
|
|
client *redisv9.Client
|
|
}
|
|
|
|
// NewClient creates a new Redis client.
|
|
func NewClient(config Config) (*Client, error) {
|
|
if err := validateConfig(config); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
client := redisv9.NewClient(&redisv9.Options{
|
|
Addr: config.Addr(),
|
|
Password: config.Password,
|
|
DB: config.DB,
|
|
PoolSize: config.PoolSize,
|
|
MinIdleConns: config.MinIdleConns,
|
|
DialTimeout: config.DialTimeout,
|
|
ReadTimeout: config.ReadTimeout,
|
|
WriteTimeout: config.WriteTimeout,
|
|
})
|
|
|
|
return &Client{
|
|
config: config,
|
|
client: client,
|
|
}, nil
|
|
}
|
|
|
|
// Close closes the Redis connection.
|
|
func (c *Client) Close() error {
|
|
return c.client.Close()
|
|
}
|
|
|
|
// Ping checks if the Redis connection is alive.
|
|
func (c *Client) Ping(ctx context.Context) error {
|
|
return c.client.Ping(ctx).Err()
|
|
}
|
|
|
|
// HealthCheck performs a health check on Redis.
|
|
func (c *Client) HealthCheck(ctx context.Context) error {
|
|
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
|
defer cancel()
|
|
|
|
return c.Ping(ctx)
|
|
}
|
|
|
|
// Set stores a key-value pair with expiration.
|
|
func (c *Client) Set(ctx context.Context, key string, value interface{}, expiration time.Duration) error {
|
|
return c.client.Set(ctx, key, value, expiration).Err()
|
|
}
|
|
|
|
// Get retrieves a value by key.
|
|
func (c *Client) Get(ctx context.Context, key string) (string, error) {
|
|
return c.client.Get(ctx, key).Result()
|
|
}
|
|
|
|
// Delete removes a key.
|
|
func (c *Client) Delete(ctx context.Context, keys ...string) error {
|
|
return c.client.Del(ctx, keys...).Err()
|
|
}
|
|
|
|
// Exists checks if a key exists.
|
|
func (c *Client) Exists(ctx context.Context, key string) (bool, error) {
|
|
n, err := c.client.Exists(ctx, key).Result()
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
return n > 0, nil
|
|
}
|
|
|
|
// Incr increments a counter.
|
|
func (c *Client) Incr(ctx context.Context, key string) (int64, error) {
|
|
return c.client.Incr(ctx, key).Result()
|
|
}
|
|
|
|
// Expire sets expiration on a key.
|
|
func (c *Client) Expire(ctx context.Context, key string, expiration time.Duration) error {
|
|
return c.client.Expire(ctx, key, expiration).Err()
|
|
}
|
|
|
|
// ConfigFromEnv creates a Config from environment variables.
|
|
func ConfigFromEnv() Config {
|
|
cfg := DefaultConfig()
|
|
|
|
cfg.Host = getenvString("REDIS_HOST", cfg.Host)
|
|
cfg.Port = getenvInt("REDIS_PORT", cfg.Port)
|
|
cfg.Password = getenvString("REDIS_PASSWORD", cfg.Password)
|
|
cfg.DB = getenvInt("REDIS_DB", cfg.DB)
|
|
cfg.PoolSize = getenvInt("REDIS_POOL_SIZE", cfg.PoolSize)
|
|
cfg.MinIdleConns = getenvInt("REDIS_MIN_IDLE_CONNS", cfg.MinIdleConns)
|
|
cfg.DialTimeout = getenvDuration("REDIS_DIAL_TIMEOUT", cfg.DialTimeout)
|
|
cfg.ReadTimeout = getenvDuration("REDIS_READ_TIMEOUT", cfg.ReadTimeout)
|
|
cfg.WriteTimeout = getenvDuration("REDIS_WRITE_TIMEOUT", cfg.WriteTimeout)
|
|
|
|
return cfg
|
|
}
|
|
|
|
func validateConfig(c Config) error {
|
|
switch {
|
|
case c.Host == "":
|
|
return errors.New("redis host is required")
|
|
case c.Port <= 0:
|
|
return errors.New("redis port must be greater than 0")
|
|
case c.PoolSize <= 0:
|
|
return errors.New("redis pool size must be greater than 0")
|
|
case c.MinIdleConns < 0:
|
|
return errors.New("redis min idle conns must be >= 0")
|
|
case c.DialTimeout <= 0:
|
|
return errors.New("redis dial timeout must be greater than 0")
|
|
case c.ReadTimeout <= 0:
|
|
return errors.New("redis read timeout must be greater than 0")
|
|
case c.WriteTimeout <= 0:
|
|
return errors.New("redis write timeout must be greater than 0")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func getenvString(key string, fallback string) string {
|
|
if v := os.Getenv(key); v != "" {
|
|
return v
|
|
}
|
|
return fallback
|
|
}
|
|
|
|
func getenvInt(key string, fallback int) int {
|
|
v := os.Getenv(key)
|
|
if v == "" {
|
|
return fallback
|
|
}
|
|
|
|
i, err := strconv.Atoi(v)
|
|
if err != nil {
|
|
return fallback
|
|
}
|
|
return i
|
|
}
|
|
|
|
func getenvDuration(key string, fallback time.Duration) time.Duration {
|
|
v := os.Getenv(key)
|
|
if v == "" {
|
|
return fallback
|
|
}
|
|
|
|
d, err := time.ParseDuration(v)
|
|
if err != nil {
|
|
return fallback
|
|
}
|
|
return d
|
|
}
|