services/explorer/consumer/fetcher/tokenprice/cache.go
package tokenprice
import (
"context"
"fmt"
lru "github.com/hashicorp/golang-lru/v2"
"github.com/synapsecns/sanguine/services/explorer/consumer/fetcher"
"time"
)
// Service provides price data about tokens using either a cache or defillama
// cache keys sare always ${coin gecko id}_${timestamp}.
type Service interface {
// GetPriceData attempts to get price data from the cache otherwise it requests defillama
GetPriceData(context.Context, int, string) *float64
}
const cacheSize = 5000
type tokenPriceServiceImpl struct {
// tokenCache is the cache of the token prices
tokenPriceCache *lru.TwoQueueCache[string, float64]
}
// NewPriceDataService creates a new token price data service.
func NewPriceDataService() (Service, error) {
cache, err := lru.New2Q[string, float64](cacheSize)
if err != nil {
return nil, fmt.Errorf("could not create token price data cache: %w", err)
}
return &tokenPriceServiceImpl{
tokenPriceCache: cache,
}, nil
}
func (t *tokenPriceServiceImpl) GetPriceData(parentCtx context.Context, timestamp int, coinGeckoID string) *float64 {
ctx, cancel := context.WithTimeout(parentCtx, 3*time.Minute)
defer cancel()
key := fmt.Sprintf("%s_%d", coinGeckoID, timestamp)
// truncate key to save requests
key = key[:len(key)-3]
if data, ok := t.tokenPriceCache.Get(key); ok {
return &data
}
tokenPrice := fetcher.GetDefiLlamaData(ctx, timestamp, coinGeckoID)
if tokenPrice == nil {
return nil
}
t.tokenPriceCache.Add(key, *tokenPrice)
return tokenPrice
}