feat_: add in-memory caching support for token market values

This commit is contained in:
Sean Hagstrom 2024-09-13 12:17:09 +01:00
parent 13ab5b6f24
commit 5cc39ac272
No known key found for this signature in database
GPG Key ID: 5257FEDF56307320
1 changed files with 87 additions and 0 deletions

View File

@ -23,12 +23,21 @@ type DataPoint struct {
UpdatedAt int64
}
type MarketValuesSnapshot struct {
MarketValues thirdparty.TokenMarketValues
UpdatedAt int64
}
type DataPerTokenAndCurrency = map[string]map[string]DataPoint
type MarketValuesPerToken = map[string]MarketValuesSnapshot
type MarketValuesPerCurrencyAndToken = map[string]MarketValuesPerToken
type Manager struct {
feed *event.Feed
priceCache DataPerTokenAndCurrency
priceCacheLock sync.RWMutex
marketCache MarketValuesPerCurrencyAndToken
marketCacheLock sync.RWMutex
IsConnected bool
LastCheckedAt int64
IsConnectedLock sync.RWMutex
@ -47,6 +56,7 @@ func NewManager(providers []thirdparty.MarketDataProvider, feed *event.Feed) *Ma
return &Manager{
feed: feed,
priceCache: make(DataPerTokenAndCurrency),
marketCache: make(MarketValuesPerCurrencyAndToken),
IsConnected: true,
LastCheckedAt: time.Now().Unix(),
circuitbreaker: cb,
@ -133,9 +143,86 @@ func (pm *Manager) FetchTokenMarketValues(symbols []string, currency string) (ma
}
marketValues := result.(map[string]thirdparty.TokenMarketValues)
pm.updateMarketCache(currency, marketValues)
return marketValues, nil
}
func (pm *Manager) GetCachedTokenMarketValues() MarketValuesPerCurrencyAndToken {
pm.marketCacheLock.RLock()
defer pm.marketCacheLock.RUnlock()
return pm.marketCache
}
func (pm *Manager) GetOrFetchTokenMarketValues(symbols []string, currency string, maxAgeInSeconds int64) (map[string]thirdparty.TokenMarketValues, error) {
symbolsToFetchMap := make(map[string]bool)
symbolsToFetch := make([]string, 0, len(symbols))
now := time.Now().Unix()
tokenMarketValuesCache, ok := pm.GetCachedTokenMarketValues()[currency]
if !ok {
return pm.FetchTokenMarketValues(symbols, currency)
}
for _, symbol := range symbols {
marketValueSnapshot, found := tokenMarketValuesCache[symbol]
if !found {
if !symbolsToFetchMap[symbol] {
symbolsToFetchMap[symbol] = true
symbolsToFetch = append(symbolsToFetch, symbol)
}
continue
}
if now-marketValueSnapshot.UpdatedAt > maxAgeInSeconds {
if !symbolsToFetchMap[symbol] {
symbolsToFetchMap[symbol] = true
symbolsToFetch = append(symbolsToFetch, symbol)
}
continue
}
}
if len(symbolsToFetch) > 0 {
_, err := pm.FetchTokenMarketValues(symbolsToFetch, currency)
if err != nil {
return nil, err
}
}
tokenMarketValues := pm.getCachedTokenMarketValuesFor(currency, symbols)
return tokenMarketValues, nil
}
func (pm *Manager) updateMarketCache(currency string, marketValuesPerToken map[string]thirdparty.TokenMarketValues) {
pm.marketCacheLock.Lock()
defer pm.marketCacheLock.Unlock()
for token, tokenMarketValues := range marketValuesPerToken {
_, present := pm.marketCache[currency]
if !present {
pm.marketCache[currency] = make(map[string]MarketValuesSnapshot)
}
pm.marketCache[currency][token] = MarketValuesSnapshot{
UpdatedAt: time.Now().Unix(),
MarketValues: tokenMarketValues,
}
}
}
func (pm *Manager) getCachedTokenMarketValuesFor(currency string, symbols []string) map[string]thirdparty.TokenMarketValues {
tokenMarketValues := make(map[string]thirdparty.TokenMarketValues)
if cachedTokenMarketValues, ok := pm.marketCache[currency]; ok {
for _, symbol := range symbols {
if marketValuesSnapshot, found := cachedTokenMarketValues[symbol]; found {
tokenMarketValues[symbol] = marketValuesSnapshot.MarketValues
}
}
}
return tokenMarketValues
}
func (pm *Manager) FetchTokenDetails(symbols []string) (map[string]thirdparty.TokenDetails, error) {
result, err := pm.makeCall(pm.providers, func(provider thirdparty.MarketDataProvider) (interface{}, error) {
return provider.FetchTokenDetails(symbols)