Files
mal/integrations/jikan/cache/store.go

87 lines
1.9 KiB
Go

package cache
import (
"context"
"encoding/json"
"time"
"mal/internal/db"
"mal/internal/observability"
)
type Store struct {
db db.Querier
metrics *observability.Metrics
}
func NewStore(queries db.Querier, metrics *observability.Metrics) *Store {
return &Store{db: queries, metrics: metrics}
}
// Get retrieves a fresh cached value by key.
func (s *Store) Get(parentCtx context.Context, key string, out any) bool {
ctx, cancel := context.WithTimeout(parentCtx, 2*time.Second)
defer cancel()
data, err := s.db.GetJikanCache(ctx, key)
if err != nil {
s.metrics.ObserveCache("jikan", "miss")
return false
}
if err := json.Unmarshal([]byte(data), out); err != nil {
s.metrics.ObserveCache("jikan", "miss")
return false
}
s.metrics.ObserveCache("jikan", "hit")
return true
}
// GetStale retrieves an expired-but-available cached value by key.
func (s *Store) GetStale(parentCtx context.Context, key string, out any) bool {
ctx, cancel := context.WithTimeout(parentCtx, 2*time.Second)
defer cancel()
data, err := s.db.GetJikanCacheStale(ctx, key)
if err != nil {
s.metrics.ObserveCache("jikan_stale", "miss")
return false
}
if err := json.Unmarshal([]byte(data), out); err != nil {
s.metrics.ObserveCache("jikan_stale", "miss")
return false
}
s.metrics.ObserveCache("jikan_stale", "hit")
return true
}
// Set stores data in cache with the specified TTL.
func (s *Store) Set(parentCtx context.Context, key string, data any, ttl time.Duration) {
ctx, cancel := context.WithTimeout(parentCtx, 2*time.Second)
defer cancel()
bytes, err := json.Marshal(data)
if err != nil {
return
}
err = s.db.SetJikanCache(ctx, db.SetJikanCacheParams{
Key: key,
Data: string(bytes),
ExpiresAt: time.Now().Add(ttl),
})
if err != nil {
observability.LogJSON(
observability.LogLevelError,
"jikan_cache_set",
"jikan",
"",
map[string]any{"cache_key": key},
err,
)
}
}