ui
This commit is contained in:
61
api/internal/cache/cache.go
vendored
Normal file
61
api/internal/cache/cache.go
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/redis/go-redis/v9"
|
||||
)
|
||||
|
||||
// Cache is a minimal interface for key/value byte storage with TTL.
|
||||
// Both the Valkey implementation and the no-op satisfy it.
|
||||
type Cache interface {
|
||||
Get(ctx context.Context, key string) ([]byte, bool)
|
||||
Set(ctx context.Context, key string, value []byte, ttl time.Duration)
|
||||
}
|
||||
|
||||
// valkeyCache wraps a go-redis client connected to a Valkey (or Redis) server.
|
||||
type valkeyCache struct {
|
||||
client *redis.Client
|
||||
}
|
||||
|
||||
// NewValkeyCache parses url (e.g. "redis://localhost:6379"), pings the server,
|
||||
// and returns a ready Cache. Returns an error if the server is unreachable.
|
||||
func NewValkeyCache(url string) (Cache, error) {
|
||||
opts, err := redis.ParseURL(url)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client := redis.NewClient(opts)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
if err := client.Ping(ctx).Err(); err != nil {
|
||||
client.Close()
|
||||
return nil, err
|
||||
}
|
||||
return &valkeyCache{client: client}, nil
|
||||
}
|
||||
|
||||
func (c *valkeyCache) Get(ctx context.Context, key string) ([]byte, bool) {
|
||||
val, err := c.client.Get(ctx, key).Bytes()
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
return val, true
|
||||
}
|
||||
|
||||
func (c *valkeyCache) Set(ctx context.Context, key string, value []byte, ttl time.Duration) {
|
||||
// Best-effort: cache errors are non-fatal.
|
||||
c.client.Set(ctx, key, value, ttl)
|
||||
}
|
||||
|
||||
// noopCache is used when VALKEY_URL is not configured.
|
||||
// All Gets miss; all Sets are discarded.
|
||||
type noopCache struct{}
|
||||
|
||||
// NewNoopCache returns a Cache that never stores anything.
|
||||
func NewNoopCache() Cache { return &noopCache{} }
|
||||
|
||||
func (c *noopCache) Get(_ context.Context, _ string) ([]byte, bool) { return nil, false }
|
||||
func (c *noopCache) Set(_ context.Context, _ string, _ []byte, _ time.Duration) {
|
||||
}
|
||||
34
api/internal/cache/key.go
vendored
Normal file
34
api/internal/cache/key.go
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
// HashFile computes the SHA-256 hex digest of the file at path.
|
||||
func HashFile(path string) (string, error) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer f.Close()
|
||||
h := sha256.New()
|
||||
if _, err := io.Copy(h, f); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fmt.Sprintf("%x", h.Sum(nil)), nil
|
||||
}
|
||||
|
||||
// AnalysisKey builds a cache key for FFF or SLA analysis.
|
||||
// Format: "<endpoint>:<filehash>:scale=<scale>"
|
||||
// scale uses %.6g so "1.50" and "1.5" map to the same key.
|
||||
func AnalysisKey(endpoint, fileHash string, scale float64) string {
|
||||
return fmt.Sprintf("%s:%s:scale=%.6g", endpoint, fileHash, scale)
|
||||
}
|
||||
|
||||
// ThumbnailKey builds a cache key for thumbnail generation.
|
||||
func ThumbnailKey(fileHash string) string {
|
||||
return "thumbnail:" + fileHash
|
||||
}
|
||||
Reference in New Issue
Block a user