91 lines
2.8 KiB
Go
91 lines
2.8 KiB
Go
package indexer
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
"github.com/jackc/pgx/v5"
|
|
"github.com/riverqueue/river"
|
|
"github.com/rs/zerolog/log"
|
|
|
|
"homelab.lan/music-agregator/internal/config"
|
|
)
|
|
|
|
type CachedIndexer struct {
|
|
inner Indexer
|
|
cache *IndexerCache
|
|
riverClient *river.Client[pgx.Tx]
|
|
cfg config.CacheConfig
|
|
}
|
|
|
|
func NewCachedIndexer(inner Indexer, cache *IndexerCache, riverClient *river.Client[pgx.Tx], cfg config.CacheConfig) *CachedIndexer {
|
|
return &CachedIndexer{
|
|
inner: inner,
|
|
cache: cache,
|
|
riverClient: riverClient,
|
|
cfg: cfg,
|
|
}
|
|
}
|
|
|
|
func (c *CachedIndexer) Search(query string, limit int32, tracker string) (SearchResult, error) {
|
|
key := query + "|" + tracker
|
|
log.Trace().Str("key", key).Str("query", query).Str("tracker", tracker).Msg("cached indexer search")
|
|
|
|
if entry, ok := c.cache.Get(key); ok {
|
|
log.Debug().Str("key", key).Int("items", len(entry.Result.Items)).Msg("returning cached result")
|
|
return entry.Result, nil
|
|
}
|
|
|
|
log.Trace().Str("key", key).Msg("cache miss, fetching from indexer")
|
|
result, err := c.inner.Search(query, limit, tracker)
|
|
if err != nil {
|
|
log.Error().Err(err).Str("key", key).Msg("cached indexer fetch failed")
|
|
return SearchResult{}, err
|
|
}
|
|
|
|
url := c.inner.BuildSearchURL(query, limit, tracker)
|
|
log.Trace().Str("key", key).Str("url", url).Int("items", len(result.Items)).Msg("caching result")
|
|
|
|
c.cache.Add(CacheEntry{
|
|
Key: key,
|
|
URL: url,
|
|
Result: result,
|
|
CreatedAt: time.Now(),
|
|
TTL: c.cfg.TTL,
|
|
RefreshInterval: c.cfg.RefreshInterval,
|
|
})
|
|
|
|
scheduleAt := time.Now().Add(c.cfg.RefreshInterval)
|
|
_, err = c.riverClient.Insert(context.Background(), CacheRefreshArgs{
|
|
Key: key,
|
|
URL: url,
|
|
TTLExpires: time.Now().Add(c.cfg.TTL),
|
|
RefreshInterval: c.cfg.RefreshInterval,
|
|
}, &river.InsertOpts{
|
|
ScheduledAt: scheduleAt,
|
|
})
|
|
if err != nil {
|
|
log.Error().Err(err).Str("key", key).Msg("failed to schedule cache refresh job")
|
|
} else {
|
|
log.Debug().Str("key", key).Time("scheduled_at", scheduleAt).Msg("cache refresh job scheduled")
|
|
}
|
|
|
|
log.Debug().Str("key", key).Dur("ttl", c.cfg.TTL).Dur("refresh", c.cfg.RefreshInterval).Int("items", len(result.Items)).Msg("cached indexer search complete")
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func (c *CachedIndexer) FetchURL(url string) (SearchResult, error) {
|
|
log.Trace().Str("url", url).Msg("cached indexer fetch URL passthrough")
|
|
return c.inner.FetchURL(url)
|
|
}
|
|
|
|
func (c *CachedIndexer) BuildSearchURL(query string, limit int32, tracker string) string {
|
|
return c.inner.BuildSearchURL(query, limit, tracker)
|
|
}
|
|
|
|
func (c *CachedIndexer) Capabilities(indexerName string) (IndexerCapabilities, error) {
|
|
log.Trace().Str("indexer", indexerName).Msg("cached indexer capabilities passthrough")
|
|
return c.inner.Capabilities(indexerName)
|
|
}
|