From 6ad052031cd35f1506fd7ff17e40ac43d895ab44 Mon Sep 17 00:00:00 2001 From: Nikhil Vasan Date: Thu, 21 Mar 2024 13:17:20 -0700 Subject: [PATCH] make cpCache thread-safe + load all cps on app-start --- protocol/app/app.go | 14 +++++++++++++ .../x/prices/keeper/currency_pair_id_cache.go | 12 +++++++++-- protocol/x/prices/keeper/market.go | 10 ++++++++++ protocol/x/prices/keeper/market_param.go | 20 +++++++++++++++++++ protocol/x/prices/keeper/market_param_test.go | 2 +- protocol/x/prices/keeper/slinky_adapter.go | 2 +- 6 files changed, 56 insertions(+), 4 deletions(-) diff --git a/protocol/app/app.go b/protocol/app/app.go index 3af306366b..dcac547d0c 100644 --- a/protocol/app/app.go +++ b/protocol/app/app.go @@ -1406,6 +1406,9 @@ func New( // Hydrate the keeper in-memory data structures. app.hydrateKeeperInMemoryDataStructures() + + // load the x/prices keeper currency-pair ID cache + app.loadCurrencyPairIDsForMarkets() } app.initializeRateLimiters() @@ -1610,6 +1613,17 @@ func (app *App) DisableHealthMonitorForTesting() { app.DaemonHealthMonitor.DisableForTesting() } +// loadCurrencyPairIDsForMarkets loads the currency pair IDs for the markets from the x/prices state. +func (app *App) loadCurrencyPairIDsForMarkets() { + // Create an `uncachedCtx` where the underlying MultiStore is the `rootMultiStore`. + // We use this to load the `currencyPairIDs` with market-params from the + // x/prices state according to the underlying `rootMultiStore`. + uncachedCtx := app.BaseApp.NewUncachedContext(true, tmproto.Header{}) + + // Load the currency pair IDs for the markets from the x/prices state. + app.PricesKeeper.LoadCurrencyPairIDCache(uncachedCtx) +} + // hydrateMemStores hydrates the memStores used for caching state. func (app *App) hydrateMemStores() { // Create an `uncachedCtx` where the underlying MultiStore is the `rootMultiStore`. diff --git a/protocol/x/prices/keeper/currency_pair_id_cache.go b/protocol/x/prices/keeper/currency_pair_id_cache.go index 57657cd440..fed3781ad8 100644 --- a/protocol/x/prices/keeper/currency_pair_id_cache.go +++ b/protocol/x/prices/keeper/currency_pair_id_cache.go @@ -1,6 +1,8 @@ package keeper -import "sync" +import ( + "sync" +) // CurrencyPairIDCache handles the caching logic of currency-pairs to their corresponding IDs. This // data-structure is thread-safe, allowing concurrent reads + synchronized writes. @@ -55,8 +57,14 @@ func (c *CurrencyPairIDCache) GetIDForCurrencyPair(currencyPair string) (uint64, return id, found } -// Remove removes the currency-pair (by ID) from the cache +// Remove removes the currency-pair (by ID) from the cache. This method takes out a write lock on the +// cache or blocks until one is available before updating the cache. func (c *CurrencyPairIDCache) Remove(id uint64) { + // acquire write lock + c.Lock() + defer c.Unlock() + + // remove currency pair from cache currencyPair, found := c.idToCurrencyPair[id] if found { delete(c.idToCurrencyPair, id) diff --git a/protocol/x/prices/keeper/market.go b/protocol/x/prices/keeper/market.go index cf7bb08f2e..d424ea582c 100644 --- a/protocol/x/prices/keeper/market.go +++ b/protocol/x/prices/keeper/market.go @@ -9,6 +9,7 @@ import ( indexerevents "github.com/dydxprotocol/v4-chain/protocol/indexer/events" "github.com/dydxprotocol/v4-chain/protocol/indexer/indexer_manager" "github.com/dydxprotocol/v4-chain/protocol/lib" + "github.com/dydxprotocol/v4-chain/protocol/lib/slinky" "github.com/dydxprotocol/v4-chain/protocol/x/prices/types" ) @@ -55,6 +56,15 @@ func (k Keeper) CreateMarket( marketPriceStore := k.getMarketPriceStore(ctx) marketPriceStore.Set(lib.Uint32ToKey(marketPrice.Id), priceBytes) + // add the pair to the currency-pair-id cache + cp, err := slinky.MarketPairToCurrencyPair(marketParam.Pair) + if err != nil { + k.Logger(ctx).Error("failed to add currency pair to cache", "pair", marketParam.Pair, "err", err) + } else { + // add the pair to the currency-pair-id cache + k.currencyPairIDCache.AddCurrencyPair(uint64(marketParam.Id), cp.String()) + } + // Generate indexer event. k.GetIndexerEventManager().AddTxnEvent( ctx, diff --git a/protocol/x/prices/keeper/market_param.go b/protocol/x/prices/keeper/market_param.go index d1ab742c2f..d4992d273b 100644 --- a/protocol/x/prices/keeper/market_param.go +++ b/protocol/x/prices/keeper/market_param.go @@ -107,6 +107,26 @@ func (k Keeper) GetMarketParam( return market, true } +// LoadCurrencyPairIDCache loads the currency pair id cache from the store. +func (k Keeper) LoadCurrencyPairIDCache(ctx sdk.Context) { + marketParamStore := k.getMarketParamStore(ctx) + + iterator := marketParamStore.Iterator(nil, nil) + defer iterator.Close() + + for ; iterator.Valid(); iterator.Next() { + marketParam := types.MarketParam{} + k.cdc.MustUnmarshal(iterator.Value(), &marketParam) + + cp, err := slinky.MarketPairToCurrencyPair(marketParam.Pair) + if err == nil { + k.currencyPairIDCache.AddCurrencyPair(uint64(marketParam.Id), cp.String()) + } else { + k.Logger(ctx).Error("failed to add currency pair to cache", "pair", marketParam.Pair) + } + } +} + // GetAllMarketParams returns all market params. func (k Keeper) GetAllMarketParams(ctx sdk.Context) []types.MarketParam { marketParamStore := k.getMarketParamStore(ctx) diff --git a/protocol/x/prices/keeper/market_param_test.go b/protocol/x/prices/keeper/market_param_test.go index 0d4a5352fa..5070353ca1 100644 --- a/protocol/x/prices/keeper/market_param_test.go +++ b/protocol/x/prices/keeper/market_param_test.go @@ -47,7 +47,7 @@ func TestModifyMarketParam(t *testing.T) { } func TestModifyMarketParamUpdatesCache(t *testing.T) { - ctx, keeper, _, _, _, mockTimeProvider := keepertest.PricesKeepers(t) + ctx, keeper, _, _, mockTimeProvider := keepertest.PricesKeepers(t) mockTimeProvider.On("Now").Return(constants.TimeT) ctx = ctx.WithTxBytes(constants.TestTxBytes) diff --git a/protocol/x/prices/keeper/slinky_adapter.go b/protocol/x/prices/keeper/slinky_adapter.go index 5276d7d4f5..a4e4c33d88 100644 --- a/protocol/x/prices/keeper/slinky_adapter.go +++ b/protocol/x/prices/keeper/slinky_adapter.go @@ -41,7 +41,7 @@ func (k Keeper) GetCurrencyPairFromID(ctx sdk.Context, id uint64) (cp slinkytype k.Logger(ctx).Error("CurrencyPairFromString", "error", err) return cp, false } - + return cp, true }