Files
History_Api/pkg/cache/redis.go
AzenKain 79199f627d
Some checks failed
Build and Release / release (push) Failing after 51s
init
2026-03-25 22:29:43 +07:00

138 lines
3.3 KiB
Go

package cache
import (
"context"
"encoding/json"
"fmt"
"history-api/pkg/config"
"time"
"github.com/redis/go-redis/v9"
)
type Cache interface {
Set(ctx context.Context, key string, value any, ttl time.Duration) error
Get(ctx context.Context, key string, dest any) error
Del(ctx context.Context, keys ...string) error
DelByPattern(ctx context.Context, pattern string) error
MGet(ctx context.Context, keys ...string) [][]byte
MSet(ctx context.Context, pairs map[string]any, ttl time.Duration) error
}
type RedisClient struct {
client *redis.Client
}
func NewRedisClient() (Cache, error) {
uri, err := config.GetConfig("REDIS_CONNECTION_URI")
if err != nil {
return nil, err
}
rdb := redis.NewClient(&redis.Options{
Addr: uri,
MinIdleConns: 10,
DialTimeout: 5 * time.Second,
ReadTimeout: 3 * time.Second,
WriteTimeout: 3 * time.Second,
MaxRetries: 3,
MinRetryBackoff: 8 * time.Millisecond,
MaxRetryBackoff: 512 * time.Millisecond,
DisableIdentity: true,
})
if err := rdb.Ping(context.Background()).Err(); err != nil {
return nil, fmt.Errorf("could not connect to Redis: %v", err)
}
return &RedisClient{client: rdb}, nil
}
func (r *RedisClient) Del(ctx context.Context, keys ...string) error {
if len(keys) == 0 {
return nil
}
return r.client.Del(ctx, keys...).Err()
}
func (r *RedisClient) DelByPattern(ctx context.Context, pattern string) error {
var cursor uint64
for {
keys, nextCursor, err := r.client.Scan(ctx, cursor, pattern, 100).Result()
if err != nil {
return fmt.Errorf("error scanning keys with pattern %s: %v", pattern, err)
}
if len(keys) > 0 {
if err := r.client.Del(ctx, keys...).Err(); err != nil {
return fmt.Errorf("error deleting keys during scan: %v", err)
}
}
cursor = nextCursor
if cursor == 0 {
break
}
}
return nil
}
func (r *RedisClient) Set(ctx context.Context, key string, value any, ttl time.Duration) error {
data, err := json.Marshal(value)
if err != nil {
return err
}
return r.client.Set(ctx, key, data, ttl).Err()
}
func (r *RedisClient) Get(ctx context.Context, key string, dest any) error {
data, err := r.client.Get(ctx, key).Bytes()
if err != nil {
return err
}
return json.Unmarshal(data, dest)
}
func (r *RedisClient) MSet(ctx context.Context, pairs map[string]any, ttl time.Duration) error {
pipe := r.client.Pipeline()
for key, value := range pairs {
data, err := json.Marshal(value)
if err != nil {
return fmt.Errorf("failed to marshal key %s: %v", key, err)
}
pipe.Set(ctx, key, data, ttl)
}
_, err := pipe.Exec(ctx)
return err
}
func (r *RedisClient) MGet(ctx context.Context, keys ...string) [][]byte {
res, err := r.client.MGet(ctx, keys...).Result()
if err != nil {
return nil
}
results := make([][]byte, len(res))
for i, val := range res {
if val != nil {
results[i] = []byte(val.(string))
}
}
return results
}
func GetMultiple[T any](ctx context.Context, c Cache, keys []string) ([]T, error) {
raws := c.MGet(ctx, keys...)
final := make([]T, 0)
for _, b := range raws {
if b == nil {
continue
}
var item T
if err := json.Unmarshal(b, &item); err == nil {
final = append(final, item)
}
}
return final, nil
}