diff --git a/cmd/main.go b/cmd/main.go index 04236f4..c224494 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -25,7 +25,7 @@ func main() { // Connect to database db, err := database.Connect(db_config.GetDSN()) if err != nil { - log.Error("Failed to connect to database: ", err) + log.Error("Error", "error", err) } defer db.Close() diff --git a/internal/cache/redis.go b/internal/cache/redis.go index 1bfed7d..b06f340 100644 --- a/internal/cache/redis.go +++ b/internal/cache/redis.go @@ -3,27 +3,69 @@ package cache import ( "context" "fmt" + "sync/atomic" "time" + "github.com/charmbracelet/log" "github.com/redis/go-redis/v9" ) type Cache struct { - Client *redis.Client + Client *redis.Client + Enabled atomic.Bool } // CreateCache creates a new Redis Cache func CreateCache(host string, port int, db int) *Cache { address := fmt.Sprintf("%s:%d", host, port) rdb := redis.NewClient(&redis.Options{ - Addr: address, - Password: "", - DB: db, + Addr: address, + Password: "", + DB: db, + MaxRetries: 1, }) - return &Cache{ + c := &Cache{ Client: rdb, } + + // Initial check to set the state + ctx, cancel := context.WithTimeout(context.Background(), time.Second*2) + defer cancel() + + if err := rdb.Ping(ctx).Err(); err != nil { + log.Warn("Redis is offline. Starting in disabled mode.", "error", err) + c.Enabled.Store(false) + } else { + c.Enabled.Store(true) + } + + // Start background health checker + go c.startHealthCheck() + + return c +} + +// startHealthCheck periodically checks the status of redis and changes status +func (c *Cache) startHealthCheck() { + ticker := time.NewTicker(1 * time.Minute) + for range ticker.C { + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + err := c.Client.Ping(ctx).Err() + cancel() + + if err != nil { + if c.Enabled.Load() { + log.Error("Redis connection lost. Disabling cache.") + c.Enabled.Store(false) + } + } else { + if !c.Enabled.Load() { + log.Info("Redis connection restored. Enabling cache.") + c.Enabled.Store(true) + } + } + } } // Set sets a key value in the cache with an expiry @@ -33,7 +75,17 @@ func (c *Cache) Set(ctx context.Context, key string, value any, expiration time. // Get returns a key value from the cache if there is any func (c *Cache) Get(ctx context.Context, key string) (string, error) { - return c.Client.Get(ctx, key).Result() + val, err := c.Client.Get(ctx, key).Result() + if err != nil { + // Handle key not found + if err == redis.Nil { + // Return empty key and return descriptive error + return "", fmt.Errorf("key: %s not found. Error: %w", key, err) + } + // Handle any other errors + return "", fmt.Errorf("redis GET %s failed: %w", key, err) + } + return val, nil } // Clear clears specific keys from the cache