Files
test_deploy/internal/cache/valkey.go
Alex Shevchuk d84487d238 1
2025-08-18 17:12:04 +03:00

144 lines
3.1 KiB
Go

package cache
import (
"context"
"crypto/tls"
"crypto/x509"
"fmt"
"os"
"time"
"github.com/go-redis/redis/v7"
)
type ValKeyCacheConfig struct {
Addrs []string
Password string
ReadOnly bool
DialTimeout time.Duration
PoolSize int
DefaultTTL time.Duration
RootCaFilePath string
}
type valkeyClient interface {
Set(key string, value any, expiration time.Duration) *redis.StatusCmd
Get(key string) *redis.StringCmd
Del(keys ...string) *redis.IntCmd
Exists(keys ...string) *redis.IntCmd
Ping() *redis.StatusCmd
}
type valKeyCache struct {
config ValKeyCacheConfig
client valkeyClient
}
func newValKeyCache(c Config) (*valKeyCache, error) {
cfg, ok := c.(ValKeyCacheConfig)
if !ok {
return nil, ErrInvalidConfig
}
caCert, err := os.ReadFile(cfg.RootCaFilePath)
if err != nil {
return nil, fmt.Errorf("%w: %s", ErrTlsConfig, err.Error())
}
caCertPool := x509.NewCertPool()
if ok := caCertPool.AppendCertsFromPEM(caCert); !ok {
return nil, fmt.Errorf("%w: %s", ErrTlsConfig, "failed to append CA cert to pool")
}
tlsConfig := &tls.Config{
RootCAs: caCertPool,
MinVersion: tls.VersionTLS12,
}
client := redis.NewUniversalClient(&redis.UniversalOptions{
Addrs: cfg.Addrs,
Password: cfg.Password,
ReadOnly: cfg.ReadOnly,
DialTimeout: cfg.DialTimeout,
PoolSize: cfg.PoolSize,
TLSConfig: tlsConfig,
})
if err := client.Ping().Err(); err != nil {
return nil, fmt.Errorf("%w: %v", ErrConnect, err)
}
return &valKeyCache{
config: cfg,
client: client,
}, nil
}
func (c *valKeyCache) Get(ctx context.Context, key string, valueType ValueType) (string, error) {
if err := c.checkInstance(); err != nil {
return "", fmt.Errorf("%w: %v", ErrGet, err)
}
val, err := c.client.Get(getKey(key, valueType)).Result()
if err == redis.Nil {
return "", ErrKeyNotFound
}
if err != nil {
return "", fmt.Errorf("%w: %v", ErrGet, err)
}
return val, nil
}
func (c *valKeyCache) Set(ctx context.Context, key string, valueType ValueType, value any, expiration time.Duration) error {
if err := c.checkInstance(); err != nil {
return fmt.Errorf("%w: %v", ErrSet, err)
}
key = getKey(key, valueType)
if exists, err := c.client.Exists(key).Result(); err != nil || exists == 1 {
if exists == 1 {
return ErrKeyExists
}
return fmt.Errorf("%w: %v", ErrSet, err)
}
if err := c.client.Set(key, value, expiration).Err(); err != nil {
return fmt.Errorf("%w: %v", ErrSet, err)
}
return nil
}
func (c *valKeyCache) Del(ctx context.Context, key string, valueType ValueType) error {
if err := c.checkInstance(); err != nil {
return fmt.Errorf("%w: %v", ErrDel, err.Error())
}
if res, err := c.client.Del(getKey(key, valueType)).Result(); err != nil || res == 0 {
if res == 0 {
return ErrKeyNotFound
}
return fmt.Errorf("%w: %s", ErrDel, err.Error())
}
return nil
}
func (c *valKeyCache) checkInstance() error {
if c == nil {
return ErrInvalidInstance
}
if s := c.client.Ping(); s.Err() != nil {
return fmt.Errorf("%w: %v", ErrInvalidInstance, s.Err().Error())
}
return nil
}