refactor: remove metrics from jikan client

This commit is contained in:
2026-06-23 15:08:46 +02:00
committed by Milas Holsting
parent 546ab66b1a
commit 0d53d5efdc
4 changed files with 10 additions and 46 deletions

View File

@@ -10,32 +10,27 @@ import (
)
type Store struct {
db db.Querier
metrics *observability.Metrics
db db.Querier
}
func NewStore(queries db.Querier, metrics *observability.Metrics) *Store {
return &Store{db: queries, metrics: metrics}
func NewStore(queries db.Querier) *Store {
return &Store{db: queries}
}
// 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()
defer s.observeStats(parentCtx)
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
}
@@ -43,20 +38,16 @@ func (s *Store) Get(parentCtx context.Context, key string, out any) bool {
func (s *Store) GetStale(parentCtx context.Context, key string, out any) bool {
ctx, cancel := context.WithTimeout(parentCtx, 2*time.Second)
defer cancel()
defer s.observeStats(parentCtx)
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
}
@@ -64,7 +55,6 @@ func (s *Store) GetStale(parentCtx context.Context, key string, out any) bool {
func (s *Store) Set(parentCtx context.Context, key string, data any, ttl time.Duration) {
ctx, cancel := context.WithTimeout(parentCtx, 2*time.Second)
defer cancel()
defer s.observeStats(parentCtx)
bytes, err := json.Marshal(data)
if err != nil {
@@ -87,22 +77,3 @@ func (s *Store) Set(parentCtx context.Context, key string, data any, ttl time.Du
)
}
}
func (s *Store) observeStats(parentCtx context.Context) {
ctx, cancel := context.WithTimeout(parentCtx, 2*time.Second)
defer cancel()
stats, err := s.db.GetJikanCacheStats(ctx)
if err != nil {
observability.LogJSON(
observability.LogLevelWarn,
"jikan_cache_stats",
"jikan",
"",
nil,
err,
)
return
}
s.metrics.ObserveJikanCacheStats(stats.TotalRows, stats.ExpiredRows, stats.OldestExpiresAtSeconds)
}

View File

@@ -39,21 +39,20 @@ const jikanSlowLogThreshold = 750 * time.Millisecond
type APIError = jtransport.APIError
func NewClient(cfg config.Config, queries *db.Queries, metrics *observability.Metrics) *Client {
func NewClient(cfg config.Config, queries *db.Queries) *Client {
limiter := rate.NewLimiter(400 * time.Millisecond)
client := &Client{
baseURL: "https://api.jikan.moe/v4",
db: queries,
retrySignal: make(chan struct{}, 1),
refreshSem: make(chan struct{}, 4),
cache: jcache.NewStore(queries, metrics),
cache: jcache.NewStore(queries),
traceEnabled: cfg.JikanTrace,
randomPool: make([]Anime, 0),
}
client.fetcher = jtransport.NewClient(jtransport.Config{
HTTPClient: jtransport.NewHTTPClient(),
Limiter: limiter,
Metrics: metrics,
TraceEnabled: client.jikanTraceEnabled,
})

View File

@@ -7,7 +7,6 @@ import (
"io"
"mal/internal/config"
"mal/internal/db"
"mal/internal/observability"
"net/http"
"strings"
"testing"
@@ -31,7 +30,7 @@ func TestGetWithCacheReturnsStaleAndRefreshesAsync(t *testing.T) {
}()
queries := db.New(sqlDB)
client := NewClient(config.Config{}, queries, observability.NewMetrics())
client := NewClient(config.Config{}, queries)
stale := TopAnimeResponse{Data: []Anime{{MalID: 1, Title: "stale"}}}
insertCachedResponse(t, sqlDB, "top:1", stale, time.Now().Add(-time.Hour))
@@ -65,7 +64,7 @@ func TestGetWithCacheAllowsEmptySearchResults(t *testing.T) {
}()
queries := db.New(sqlDB)
client := NewClient(config.Config{}, queries, observability.NewMetrics())
client := NewClient(config.Config{}, queries)
client.fetcher.HTTPClient = &http.Client{
Transport: roundTripFunc(func(*http.Request) (*http.Response, error) {
body := `{"pagination":{"has_next_page":false},"data":[]}`
@@ -95,7 +94,7 @@ func TestLoadCachedRandomPoolIgnoresExpiredAnimeCache(t *testing.T) {
}()
queries := db.New(sqlDB)
client := NewClient(config.Config{}, queries, observability.NewMetrics())
client := NewClient(config.Config{}, queries)
insertCachedAnime(t, sqlDB, "anime:1", Anime{MalID: 1, Title: "fresh"}, time.Now().Add(time.Hour))
insertCachedAnime(t, sqlDB, "anime:2", Anime{MalID: 2, Title: "expired"}, time.Now().Add(-time.Hour))

View File

@@ -22,14 +22,12 @@ const slowLogThreshold = 750 * time.Millisecond
type Client struct {
HTTPClient *http.Client
Limiter *rate.Limiter
Metrics *observability.Metrics
TraceEnabled func() bool
}
type Config struct {
HTTPClient *http.Client
Limiter *rate.Limiter
Metrics *observability.Metrics
TraceEnabled func() bool
}
@@ -58,7 +56,6 @@ func NewClient(cfg Config) *Client {
return &Client{
HTTPClient: cfg.HTTPClient,
Limiter: cfg.Limiter,
Metrics: cfg.Metrics,
TraceEnabled: cfg.TraceEnabled,
}
}
@@ -94,12 +91,10 @@ func (c *Client) FetchWithRetry(ctx context.Context, urlStr string, out any) err
maxRetries := 5
startedAt := time.Now()
attempts := 0
endpoint := metricsEndpoint(urlStr)
logAndReturn := func(statusCode int, err error) error {
if isDoneContextError(ctx, err) {
return err
}
c.Metrics.ObserveJikanRequest(endpoint, statusCode, time.Since(startedAt), err)
c.logUpstream(urlStr, statusCode, attempts, startedAt, err)
return err
}
@@ -298,7 +293,7 @@ func (c *Client) logUpstream(urlStr string, statusCode int, attempts int, starte
"",
map[string]any{
"url": urlStr,
"endpoint": metricsEndpoint(urlStr),
"endpoint": endpointLabel(urlStr),
"status": statusCode,
"attempts": attempts,
"duration_ms": float64(duration.Microseconds()) / 1000,
@@ -307,7 +302,7 @@ func (c *Client) logUpstream(urlStr string, statusCode int, attempts int, starte
)
}
func metricsEndpoint(urlStr string) string {
func endpointLabel(urlStr string) string {
trimmed := strings.TrimSpace(urlStr)
if trimmed == "" {
return "unknown"