// File: /home/meninjar/goprint/service-general/internal/infrastructure/cache/redis.go package cache import ( "context" "encoding/json" "fmt" "time" "github.com/redis/go-redis/v9" ) // RedisCache implements Cache interface using Redis type RedisCache struct { client *redis.Client config CacheConfig } // NewRedisCache creates a new Redis cache instance func NewRedisCache(config CacheConfig) (Cache, error) { if !config.Enabled { return &NoOpCache{}, nil } client := redis.NewClient(&redis.Options{ Addr: fmt.Sprintf("%s:%d", config.Redis.Host, config.Redis.Port), Password: config.Redis.Password, DB: config.Redis.DB, }) // Test connection ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if err := client.Ping(ctx).Err(); err != nil { return nil, fmt.Errorf("failed to connect to Redis: %w", err) } return &RedisCache{ client: client, config: config, }, nil } // Get retrieves a value from cache func (r *RedisCache) Get(ctx context.Context, key string) (string, error) { return r.client.Get(ctx, key).Result() } // Set stores a value in cache func (r *RedisCache) Set(ctx context.Context, key string, value string, expiration time.Duration) error { return r.client.Set(ctx, key, value, expiration).Err() } // Delete removes a value from cache func (r *RedisCache) Delete(ctx context.Context, key string) error { return r.client.Del(ctx, key).Err() } // Exists checks if a key exists in cache func (r *RedisCache) Exists(ctx context.Context, key string) (bool, error) { result, err := r.client.Exists(ctx, key).Result() return result > 0, err } // GetStruct retrieves and unmarshals a struct from cache func (r *RedisCache) GetStruct(ctx context.Context, key string, dest interface{}) error { data, err := r.client.Get(ctx, key).Result() if err != nil { return err } return json.Unmarshal([]byte(data), dest) } // SetStruct marshals and stores a struct in cache func (r *RedisCache) SetStruct(ctx context.Context, key string, value interface{}, expiration time.Duration) error { data, err := json.Marshal(value) if err != nil { return fmt.Errorf("failed to marshal value: %w", err) } return r.client.Set(ctx, key, data, expiration).Err() } // Increment increments a numeric value in cache func (r *RedisCache) Increment(ctx context.Context, key string, delta int64) (int64, error) { return r.client.IncrBy(ctx, key, delta).Result() } // Decrement decrements a numeric value in cache func (r *RedisCache) Decrement(ctx context.Context, key string, delta int64) (int64, error) { return r.client.DecrBy(ctx, key, delta).Result() } // TTL returns the time to live for a key func (r *RedisCache) TTL(ctx context.Context, key string) (time.Duration, error) { return r.client.TTL(ctx, key).Result() } // GetRedisClient mendapatkan Redis client func (r *RedisCache) GetRedisClient() interface{} { return r.client } // Close closes the Redis connection func (r *RedisCache) Close() error { return r.client.Close() } // Health checks the health of the cache func (r *RedisCache) Health(ctx context.Context) error { return r.client.Ping(ctx).Err() } // MGet retrieves multiple values from cache func (r *RedisCache) MGet(ctx context.Context, keys ...string) ([]interface{}, error) { return r.client.MGet(ctx, keys...).Result() } // MSet stores multiple values in cache func (r *RedisCache) MSet(ctx context.Context, items map[string]interface{}, expiration time.Duration) error { pipe := r.client.Pipeline() for key, value := range items { data, err := json.Marshal(value) if err != nil { return fmt.Errorf("failed to marshal value for key %s: %w", key, err) } pipe.Set(ctx, key, data, expiration) } _, err := pipe.Exec(ctx) return err }