diff --git a/src/app_service/common/cache.nim b/src/app_service/common/cache.nim new file mode 100644 index 00000000000..7ba32e7e4cf --- /dev/null +++ b/src/app_service/common/cache.nim @@ -0,0 +1,22 @@ + +import tables, times + +export tables, times + +type Value*[T] = ref object + value*: T + timestamp*: DateTime + +type TimedCache*[T] = Table[string, Value[T]] + +proc newTimedCache*[T](): TimedCache[T] = initTable[string, Value[T]]() + +proc getTimestamp[T](self: TimedCache[T], cacheKey: string): DateTime = self[cacheKey].timestamp + +proc isCached*[T](self: TimedCache[T], cacheKey: string, duration=initDuration(minutes = 5)): bool = + self.hasKey(cacheKey) and ((self.getTimestamp(cacheKey) + duration) >= now()) + +proc set*[T](self: var TimedCache[T], cacheKey: string, value: T) = + self[cacheKey] = Value[T](value: value, timestamp: now()) + +proc get*[T](self: TimedCache[T], cacheKey: string): T = self[cacheKey].value \ No newline at end of file diff --git a/src/app_service/service/currency/service.nim b/src/app_service/service/currency/service.nim index 853f108b114..f98476e4ebc 100644 --- a/src/app_service/service/currency/service.nim +++ b/src/app_service/service/currency/service.nim @@ -1,7 +1,8 @@ -import NimQml, strformat, strutils +import NimQml, strformat, strutils, tables import ../settings/service as settings_service import ../token/service as token_service import ./dto, ./utils +import ../../common/cache export dto @@ -11,6 +12,9 @@ QtObject: type Service* = ref object of QObject tokenService: token_service.Service settingsService: settings_service.Service + isCurrencyFiatCache: Table[string, bool] # Fiat info does not change, we can fetch/calculate once and + fiatCurrencyFormatCache: Table[string, CurrencyFormatDto] # keep the results forever. + tokenCurrencyFormatCache: TimedCache[CurrencyFormatDto] # Token format changes with price, so we use a timed cache. proc delete*(self: Service) = self.QObject.delete @@ -23,33 +27,50 @@ QtObject: result.QObject.setup result.tokenService = tokenService result.settingsService = settingsService + result.tokenCurrencyFormatCache = newTimedCache[CurrencyFormatDto]() proc init*(self: Service) = discard + proc isCurrencyFiat(self: Service, symbol: string): bool = + if not self.isCurrencyFiatCache.hasKey(symbol): + self.isCurrencyFiatCache[symbol] = isCurrencyFiat(symbol) + return self.isCurrencyFiatCache[symbol] + proc getFiatCurrencyFormat(self: Service, symbol: string): CurrencyFormatDto = - return CurrencyFormatDto( - symbol: toUpperAscii(symbol), - displayDecimals: getFiatDisplayDecimals(symbol), - stripTrailingZeroes: false - ) + if not self.fiatCurrencyFormatCache.hasKey(symbol): + self.fiatCurrencyFormatCache[symbol] = CurrencyFormatDto( + symbol: toUpperAscii(symbol), + displayDecimals: getFiatDisplayDecimals(symbol), + stripTrailingZeroes: false + ) + return self.fiatCurrencyFormatCache[symbol] proc getTokenCurrencyFormat(self: Service, symbol: string): CurrencyFormatDto = + if self.tokenCurrencyFormatCache.isCached(symbol): + return self.tokenCurrencyFormatCache.get(symbol) + + var updateCache = true let pegSymbol = self.tokenService.getTokenPegSymbol(symbol) if pegSymbol != "": var currencyFormat = self.getFiatCurrencyFormat(pegSymbol) currencyFormat.symbol = symbol - return currencyFormat + result = currencyFormat + updateCache = true else: - let price = self.tokenService.getTokenPrice(symbol, DECIMALS_CALCULATION_CURRENCY, false) - return CurrencyFormatDto( + let price = self.tokenService.getCachedTokenPrice(symbol, DECIMALS_CALCULATION_CURRENCY) + result = CurrencyFormatDto( symbol: symbol, displayDecimals: getTokenDisplayDecimals(price), stripTrailingZeroes: true ) + updateCache = self.tokenService.isCachedTokenPriceRecent(symbol, DECIMALS_CALCULATION_CURRENCY) + + if updateCache: + self.tokenCurrencyFormatCache.set(symbol, result) proc getCurrencyFormat*(self: Service, symbol: string): CurrencyFormatDto = - if isCurrencyFiat(symbol): + if self.isCurrencyFiat(symbol): return self.getFiatCurrencyFormat(symbol) else: return self.getTokenCurrencyFormat(symbol) diff --git a/src/app_service/service/token/service.nim b/src/app_service/service/token/service.nim index 38aef871a7e..da25360fffb 100644 --- a/src/app_service/service/token/service.nim +++ b/src/app_service/service/token/service.nim @@ -11,7 +11,7 @@ import ../../../app/global/global_singleton import ../../../app/core/eventemitter import ../../../app/core/tasks/[qt, threadpool] -import ../../../backend/cache +import ../../common/cache import ./dto export dto @@ -39,7 +39,7 @@ QtObject: threadpool: ThreadPool networkService: network_service.Service tokens: Table[int, seq[TokenDto]] - priceCache: TimedCache + priceCache: TimedCache[float64] proc updateCachedTokenPrice(self: Service, crypto: string, fiat: string, price: float64) @@ -57,7 +57,7 @@ QtObject: result.threadpool = threadpool result.networkService = networkService result.tokens = initTable[int, seq[TokenDto]]() - result.priceCache = newTimedCache() + result.priceCache = newTimedCache[float64]() proc init*(self: Service) = try: @@ -130,12 +130,21 @@ QtObject: proc getTokenPriceCacheKey(crypto: string, fiat: string) : string = return renameSymbol(crypto) & renameSymbol(fiat) - proc getTokenPrice*(self: Service, crypto: string, fiat: string, fetchIfNotAvailable: bool = true): float64 = + proc isCachedTokenPriceRecent*(self: Service, crypto: string, fiat: string): bool = + let cacheKey = getTokenPriceCacheKey(crypto, fiat) + return self.priceCache.isCached(cacheKey) + + proc getCachedTokenPrice*(self: Service, crypto: string, fiat: string): float64 = let cacheKey = getTokenPriceCacheKey(crypto, fiat) - if self.priceCache.isCached(cacheKey) or (self.priceCache.hasKey(cacheKey) and not fetchIfNotAvailable): - return parseFloat(self.priceCache.get(cacheKey)) - elif not fetchIfNotAvailable: + if self.priceCache.hasKey(cacheKey): + return self.priceCache.get(cacheKey) + else: return 0.0 + + proc getTokenPrice*(self: Service, crypto: string, fiat: string, fetchIfNotAvailable: bool = true): float64 = + let cacheKey = getTokenPriceCacheKey(crypto, fiat) + if self.priceCache.isCached(cacheKey): + return self.priceCache.get(cacheKey) var prices = initTable[string, Table[string, float]]() try: @@ -143,6 +152,7 @@ QtObject: let fiatKey = renameSymbol(fiat) let response = backend.fetchPrices(@[cryptoKey], @[fiatKey]) for (symbol, pricePerCurrency) in response.result.pairs: + prices[symbol] = initTable[string, float]() for (currency, price) in pricePerCurrency.pairs: prices[symbol][currency] = price.getFloat @@ -155,7 +165,7 @@ QtObject: proc updateCachedTokenPrice(self: Service, crypto: string, fiat: string, price: float64) = let cacheKey = getTokenPriceCacheKey(crypto, fiat) - self.priceCache.set(cacheKey, $price) + self.priceCache.set(cacheKey, price) proc getTokenPegSymbol*(self: Service, symbol: string): string = for _, tokens in self.tokens: diff --git a/src/backend/cache.nim b/src/backend/cache.nim deleted file mode 100644 index 3980f2bda9a..00000000000 --- a/src/backend/cache.nim +++ /dev/null @@ -1,18 +0,0 @@ - -import tables, times - -type Value* = ref object - value*: string - timestamp*: DateTime - -type TimedCache* = Table[string, Value] - -proc newTimedCache*(): TimedCache = initTable[string, Value]() - -proc isCached*(self: TimedCache, cacheKey: string, duration=initDuration(minutes = 5)): bool = - self.hasKey(cacheKey) and ((self[cacheKey].timestamp + duration) >= now()) - -proc set*(self: var TimedCache, cacheKey: string, value: string) = - self[cacheKey] = Value(value: value, timestamp: now()) - -proc get*(self: var TimedCache, cacheKey: string): string = self[cacheKey].value \ No newline at end of file