mirror of
https://github.com/trezor/blockbook.git
synced 2026-02-20 00:51:39 +01:00
fiat rates perf optimization
This commit is contained in:
@@ -36,6 +36,10 @@ type Worker struct {
|
||||
metrics *common.Metrics
|
||||
}
|
||||
|
||||
var getTickersForTimestamps = func(fr *fiat.FiatRates, timestamps []int64, vsCurrency string, token string) (*[]*common.CurrencyRatesTicker, error) {
|
||||
return fr.GetTickersForTimestamps(timestamps, vsCurrency, token)
|
||||
}
|
||||
|
||||
// contractInfoCache is a temporary cache of contract information for ethereum token transfers
|
||||
type contractInfoCache = map[string]*bchain.ContractInfo
|
||||
|
||||
@@ -1715,23 +1719,18 @@ func (w *Worker) balanceHistoryForTxid(addrDesc bchain.AddressDescriptor, txid s
|
||||
}
|
||||
|
||||
func (w *Worker) setFiatRateToBalanceHistories(histories BalanceHistories, currencies []string) error {
|
||||
for i := range histories {
|
||||
bh := &histories[i]
|
||||
tickers, err := w.fiatRates.GetTickersForTimestamps([]int64{int64(bh.Time)}, "", "")
|
||||
if err != nil || tickers == nil || len(*tickers) == 0 {
|
||||
glog.Errorf("Error finding ticker by date %v. Error: %v", bh.Time, err)
|
||||
continue
|
||||
}
|
||||
ticker := (*tickers)[0]
|
||||
if len(histories) == 0 || w.fiatRates == nil {
|
||||
return nil
|
||||
}
|
||||
applyTickerToHistory := func(bh *BalanceHistory, ticker *common.CurrencyRatesTicker, currenciesLowercase []string) {
|
||||
if ticker == nil {
|
||||
continue
|
||||
return
|
||||
}
|
||||
if len(currencies) == 0 {
|
||||
if len(currenciesLowercase) == 0 {
|
||||
bh.FiatRates = ticker.Rates
|
||||
} else {
|
||||
rates := make(map[string]float32)
|
||||
for _, currency := range currencies {
|
||||
currency = strings.ToLower(currency)
|
||||
rates := make(map[string]float32, len(currenciesLowercase))
|
||||
for _, currency := range currenciesLowercase {
|
||||
if rate, found := ticker.Rates[currency]; found {
|
||||
rates[currency] = rate
|
||||
} else {
|
||||
@@ -1741,6 +1740,35 @@ func (w *Worker) setFiatRateToBalanceHistories(histories BalanceHistories, curre
|
||||
bh.FiatRates = rates
|
||||
}
|
||||
}
|
||||
timestamps := make([]int64, len(histories))
|
||||
for i := range histories {
|
||||
timestamps[i] = int64(histories[i].Time)
|
||||
}
|
||||
tickers, err := getTickersForTimestamps(w.fiatRates, timestamps, "", "")
|
||||
batchFetchValid := err == nil && tickers != nil && len(*tickers) == len(histories)
|
||||
if !batchFetchValid {
|
||||
glog.Errorf("Error finding tickers for %d timestamps. Error: %v", len(timestamps), err)
|
||||
}
|
||||
currenciesLowercase := make([]string, len(currencies))
|
||||
for i := range currencies {
|
||||
currenciesLowercase[i] = strings.ToLower(currencies[i])
|
||||
}
|
||||
if batchFetchValid {
|
||||
for i := range histories {
|
||||
applyTickerToHistory(&histories[i], (*tickers)[i], currenciesLowercase)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// Fallback to per-point lookup to preserve original behavior on partial failures.
|
||||
for i := range histories {
|
||||
bh := &histories[i]
|
||||
pointTickers, pointErr := getTickersForTimestamps(w.fiatRates, []int64{int64(bh.Time)}, "", "")
|
||||
if pointErr != nil || pointTickers == nil || len(*pointTickers) == 0 {
|
||||
glog.Errorf("Error finding ticker by date %v. Error: %v", bh.Time, pointErr)
|
||||
continue
|
||||
}
|
||||
applyTickerToHistory(bh, (*pointTickers)[0], currenciesLowercase)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
186
api/worker_test.go
Normal file
186
api/worker_test.go
Normal file
@@ -0,0 +1,186 @@
|
||||
//go:build unittest
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/trezor/blockbook/common"
|
||||
"github.com/trezor/blockbook/fiat"
|
||||
)
|
||||
|
||||
func TestSetFiatRateToBalanceHistories_BatchesTickerLookup(t *testing.T) {
|
||||
histories := BalanceHistories{
|
||||
{Time: 100},
|
||||
{Time: 200},
|
||||
{Time: 300},
|
||||
}
|
||||
w := &Worker{
|
||||
fiatRates: &fiat.FiatRates{Enabled: true},
|
||||
}
|
||||
originalGetter := getTickersForTimestamps
|
||||
defer func() {
|
||||
getTickersForTimestamps = originalGetter
|
||||
}()
|
||||
|
||||
calls := 0
|
||||
var gotTimestamps []int64
|
||||
getTickersForTimestamps = func(_ *fiat.FiatRates, timestamps []int64, _, _ string) (*[]*common.CurrencyRatesTicker, error) {
|
||||
calls++
|
||||
gotTimestamps = append([]int64(nil), timestamps...)
|
||||
tickers := []*common.CurrencyRatesTicker{
|
||||
{Rates: map[string]float32{"usd": 11, "eur": 22}},
|
||||
nil,
|
||||
{Rates: map[string]float32{"usd": 33}},
|
||||
}
|
||||
return &tickers, nil
|
||||
}
|
||||
|
||||
err := w.setFiatRateToBalanceHistories(histories, []string{"USD", "eur", "cad"})
|
||||
if err != nil {
|
||||
t.Fatalf("setFiatRateToBalanceHistories returned error: %v", err)
|
||||
}
|
||||
if calls != 1 {
|
||||
t.Fatalf("expected 1 ticker lookup call, got %d", calls)
|
||||
}
|
||||
if !reflect.DeepEqual(gotTimestamps, []int64{100, 200, 300}) {
|
||||
t.Fatalf("unexpected timestamps: got %v", gotTimestamps)
|
||||
}
|
||||
if !reflect.DeepEqual(histories[0].FiatRates, map[string]float32{"usd": 11, "eur": 22, "cad": -1}) {
|
||||
t.Fatalf("unexpected rates for histories[0]: %v", histories[0].FiatRates)
|
||||
}
|
||||
if histories[1].FiatRates != nil {
|
||||
t.Fatalf("expected nil rates for histories[1], got %v", histories[1].FiatRates)
|
||||
}
|
||||
if !reflect.DeepEqual(histories[2].FiatRates, map[string]float32{"usd": 33, "eur": -1, "cad": -1}) {
|
||||
t.Fatalf("unexpected rates for histories[2]: %v", histories[2].FiatRates)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetFiatRateToBalanceHistories_AllRatesWhenCurrenciesNotSpecified(t *testing.T) {
|
||||
histories := BalanceHistories{
|
||||
{Time: 100},
|
||||
}
|
||||
w := &Worker{
|
||||
fiatRates: &fiat.FiatRates{Enabled: true},
|
||||
}
|
||||
originalGetter := getTickersForTimestamps
|
||||
defer func() {
|
||||
getTickersForTimestamps = originalGetter
|
||||
}()
|
||||
|
||||
getTickersForTimestamps = func(_ *fiat.FiatRates, _ []int64, _, _ string) (*[]*common.CurrencyRatesTicker, error) {
|
||||
tickers := []*common.CurrencyRatesTicker{
|
||||
{Rates: map[string]float32{"usd": 11, "eur": 22}},
|
||||
}
|
||||
return &tickers, nil
|
||||
}
|
||||
|
||||
err := w.setFiatRateToBalanceHistories(histories, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("setFiatRateToBalanceHistories returned error: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(histories[0].FiatRates, map[string]float32{"usd": 11, "eur": 22}) {
|
||||
t.Fatalf("unexpected rates for histories[0]: %v", histories[0].FiatRates)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetFiatRateToBalanceHistories_BatchFailureFallsBackToPerPoint(t *testing.T) {
|
||||
histories := BalanceHistories{
|
||||
{Time: 100},
|
||||
{Time: 200},
|
||||
{Time: 300},
|
||||
}
|
||||
w := &Worker{
|
||||
fiatRates: &fiat.FiatRates{Enabled: true},
|
||||
}
|
||||
originalGetter := getTickersForTimestamps
|
||||
defer func() {
|
||||
getTickersForTimestamps = originalGetter
|
||||
}()
|
||||
|
||||
calls := 0
|
||||
var gotCalls [][]int64
|
||||
getTickersForTimestamps = func(_ *fiat.FiatRates, timestamps []int64, _, _ string) (*[]*common.CurrencyRatesTicker, error) {
|
||||
calls++
|
||||
gotCalls = append(gotCalls, append([]int64(nil), timestamps...))
|
||||
if len(timestamps) > 1 {
|
||||
return nil, assertError("batch error")
|
||||
}
|
||||
switch timestamps[0] {
|
||||
case 100:
|
||||
tickers := []*common.CurrencyRatesTicker{
|
||||
{Rates: map[string]float32{"usd": 11}},
|
||||
}
|
||||
return &tickers, nil
|
||||
case 200:
|
||||
return nil, assertError("point error")
|
||||
case 300:
|
||||
tickers := []*common.CurrencyRatesTicker{
|
||||
{Rates: map[string]float32{"usd": 33}},
|
||||
}
|
||||
return &tickers, nil
|
||||
default:
|
||||
tickers := []*common.CurrencyRatesTicker{}
|
||||
return &tickers, nil
|
||||
}
|
||||
}
|
||||
|
||||
err := w.setFiatRateToBalanceHistories(histories, []string{"usd"})
|
||||
if err != nil {
|
||||
t.Fatalf("setFiatRateToBalanceHistories returned error: %v", err)
|
||||
}
|
||||
if calls != 4 {
|
||||
t.Fatalf("expected 4 ticker lookup calls (1 batch + 3 point), got %d", calls)
|
||||
}
|
||||
wantCalls := [][]int64{
|
||||
{100, 200, 300},
|
||||
{100},
|
||||
{200},
|
||||
{300},
|
||||
}
|
||||
if !reflect.DeepEqual(gotCalls, wantCalls) {
|
||||
t.Fatalf("unexpected lookup calls: got %v, want %v", gotCalls, wantCalls)
|
||||
}
|
||||
if !reflect.DeepEqual(histories[0].FiatRates, map[string]float32{"usd": 11}) {
|
||||
t.Fatalf("unexpected rates for histories[0]: %v", histories[0].FiatRates)
|
||||
}
|
||||
if histories[1].FiatRates != nil {
|
||||
t.Fatalf("expected nil rates for histories[1], got %v", histories[1].FiatRates)
|
||||
}
|
||||
if !reflect.DeepEqual(histories[2].FiatRates, map[string]float32{"usd": 33}) {
|
||||
t.Fatalf("unexpected rates for histories[2]: %v", histories[2].FiatRates)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetFiatRateToBalanceHistories_SkipsLookupForEmptyHistory(t *testing.T) {
|
||||
w := &Worker{
|
||||
fiatRates: &fiat.FiatRates{Enabled: true},
|
||||
}
|
||||
originalGetter := getTickersForTimestamps
|
||||
defer func() {
|
||||
getTickersForTimestamps = originalGetter
|
||||
}()
|
||||
|
||||
calls := 0
|
||||
getTickersForTimestamps = func(_ *fiat.FiatRates, _ []int64, _, _ string) (*[]*common.CurrencyRatesTicker, error) {
|
||||
calls++
|
||||
tickers := []*common.CurrencyRatesTicker{}
|
||||
return &tickers, nil
|
||||
}
|
||||
|
||||
err := w.setFiatRateToBalanceHistories(BalanceHistories{}, []string{"usd"})
|
||||
if err != nil {
|
||||
t.Fatalf("setFiatRateToBalanceHistories returned error: %v", err)
|
||||
}
|
||||
if calls != 0 {
|
||||
t.Fatalf("expected 0 ticker lookup calls, got %d", calls)
|
||||
}
|
||||
}
|
||||
|
||||
type assertError string
|
||||
|
||||
func (e assertError) Error() string {
|
||||
return string(e)
|
||||
}
|
||||
Reference in New Issue
Block a user