144 lines
3.1 KiB
Go
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
|
|
}
|