From ee475324c5950261c9977ee025518510c648a921 Mon Sep 17 00:00:00 2001 From: Peter Bukva Date: Thu, 10 Aug 2023 08:31:19 +0100 Subject: [PATCH] Redesigning cache using atomic.Pointer + fixing tests --- x/bank/simulation/genesis.go | 18 ++++---- x/bank/simulation/genesis_test.go | 7 ++- x/mint/cache/municipal_inflation_cahe.go | 59 +++++++++++------------- x/mint/genesis.go | 2 + x/mint/simulation/genesis.go | 12 +++-- 5 files changed, 50 insertions(+), 48 deletions(-) diff --git a/x/bank/simulation/genesis.go b/x/bank/simulation/genesis.go index de45fe2ab9..c1209ed4df 100644 --- a/x/bank/simulation/genesis.go +++ b/x/bank/simulation/genesis.go @@ -13,8 +13,8 @@ import ( ) var ( - testSupplyVal, _ = sdk.NewIntFromString("1000000000000000000") - AdditionalTestSupply = sdk.NewCoins( + testSupplyVal, _ = sdk.NewIntFromString("1000000000000000000") + AdditionalTestBalancePerAccount = sdk.NewCoins( sdk.NewCoin("denom0", testSupplyVal), sdk.NewCoin("denom1", testSupplyVal), sdk.NewCoin("denom2", testSupplyVal), @@ -51,17 +51,14 @@ func RandomGenesisBalances(simState *module.SimulationState) []types.Balance { genesisBalances := []types.Balance{} for _, acc := range simState.Accounts { + coins := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(simState.InitialStake))) + coins.Add(AdditionalTestBalancePerAccount...) genesisBalances = append(genesisBalances, types.Balance{ Address: acc.Address.String(), - Coins: sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(simState.InitialStake))), + Coins: coins, }) } - if len(genesisBalances) > 0 { - nb := genesisBalances[0].Coins.Add(AdditionalTestSupply...) - genesisBalances[0].Coins = nb - } - return genesisBalances } @@ -82,7 +79,10 @@ func RandomizedGenState(simState *module.SimulationState) { numAccs := int64(len(simState.Accounts)) totalSupply := sdk.NewInt(simState.InitialStake * (numAccs + simState.NumBonded)) - supply := AdditionalTestSupply.Add(sdk.NewCoin(sdk.DefaultBondDenom, totalSupply)) + supply := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, totalSupply)) + for _, b := range AdditionalTestBalancePerAccount { + supply.Add(sdk.NewCoin(b.Denom, b.Amount.MulRaw(numAccs))) + } bankGenesis := types.GenesisState{ Params: types.Params{ diff --git a/x/bank/simulation/genesis_test.go b/x/bank/simulation/genesis_test.go index a3f7ff385a..2632450c62 100644 --- a/x/bank/simulation/genesis_test.go +++ b/x/bank/simulation/genesis_test.go @@ -44,7 +44,12 @@ func TestRandomizedGenState(t *testing.T) { require.Len(t, bankGenesis.Balances, 3) require.Equal(t, "cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r", bankGenesis.Balances[2].GetAddress().String()) require.Equal(t, "1000stake", bankGenesis.Balances[2].GetCoins().String()) - expectedSupply := simulation.AdditionalTestSupply.Add(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(6000))) + + numAccs := int64(len(simState.Accounts)) + expectedSupply := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, sdk.NewInt(6000))) + for _, b := range simulation.AdditionalTestBalancePerAccount { + expectedSupply.Add(sdk.NewCoin(b.Denom, b.Amount.MulRaw(numAccs))) + } require.Equal(t, expectedSupply.String(), bankGenesis.Supply.String()) } diff --git a/x/mint/cache/municipal_inflation_cahe.go b/x/mint/cache/municipal_inflation_cahe.go index dfc82bd15a..a52c377b3a 100644 --- a/x/mint/cache/municipal_inflation_cahe.go +++ b/x/mint/cache/municipal_inflation_cahe.go @@ -1,7 +1,7 @@ package cache import ( - "sync" + "sync/atomic" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/x/mint/types" @@ -12,11 +12,14 @@ type MunicipalInflationCacheItem struct { AnnualInflation *types.MunicipalInflation } -type MunicipalInflationCache struct { +type MunicipalInflationCacheInternal struct { blocksPerYear uint64 original *[]*types.MunicipalInflationPair inflations map[string]*MunicipalInflationCacheItem // {denom: inflationPerBlock} - mu sync.RWMutex +} + +type MunicipalInflationCache struct { + internal atomic.Pointer[MunicipalInflationCacheInternal] } // GMunicipalInflationCache Thread safety: @@ -32,41 +35,38 @@ type MunicipalInflationCache struct { var GMunicipalInflationCache = MunicipalInflationCache{} func (cache *MunicipalInflationCache) Refresh(inflations *[]*types.MunicipalInflationPair, blocksPerYear uint64) { - cache.mu.Lock() - defer cache.mu.Unlock() - - cache.refresh(inflations, blocksPerYear) + newCache := MunicipalInflationCacheInternal{} + newCache.refresh(inflations, blocksPerYear) + cache.internal.Store(&newCache) } +// RefreshIfNecessary +// IMPORTANT: Assuming *NO* concurrent writes, designed with emphasis for concurrent reads. This requirement +// guaranteed, since this method is called exclusively from call contexts which are never concurrent. +// This approach will guarantee the most possible effective cache operation in heavily concurrent +// read environment with minimum possible blocking for concurrent read operations, but with slight +// limitation for write operations (there should not be concurrent write operations). +// Most of the read operations are assumed to be done from RPC (querying municipal inflation). func (cache *MunicipalInflationCache) RefreshIfNecessary(inflations *[]*types.MunicipalInflationPair, blocksPerYear uint64) { - cache.mu.Lock() - defer cache.mu.Unlock() - - cache.refreshIfNecessary(inflations, blocksPerYear) -} - -func (cache *MunicipalInflationCache) IsRefreshRequired(blocksPerYear uint64) bool { - cache.mu.RLock() - defer cache.mu.RUnlock() - return cache.isRefreshRequired(blocksPerYear) + current := cache.internal.Load() + if current.isRefreshRequired(blocksPerYear) { + cache.Refresh(inflations, blocksPerYear) + } } func (cache *MunicipalInflationCache) GetInflation(denom string) (MunicipalInflationCacheItem, bool) { - cache.mu.RLock() - defer cache.mu.RUnlock() - infl, exists := cache.inflations[denom] + current := cache.internal.Load() + infl, exists := current.inflations[denom] return *infl, exists } func (cache *MunicipalInflationCache) GetOriginal() *[]*types.MunicipalInflationPair { - cache.mu.RLock() - defer cache.mu.RUnlock() - // NOTE(pb): Mutex locking might not be necessary here since we are returning by pointer - return cache.original + current := cache.internal.Load() + return current.original } // NOTE(pb): *NOT* thread safe -func (cache *MunicipalInflationCache) refresh(inflations *[]*types.MunicipalInflationPair, blocksPerYear uint64) { +func (cache *MunicipalInflationCacheInternal) refresh(inflations *[]*types.MunicipalInflationPair, blocksPerYear uint64) { if err := types.ValidateMunicipalInflations(inflations); err != nil { panic(err) } @@ -89,13 +89,6 @@ func (cache *MunicipalInflationCache) refresh(inflations *[]*types.MunicipalInfl } // NOTE(pb): *NOT* thread safe -func (cache *MunicipalInflationCache) refreshIfNecessary(inflations *[]*types.MunicipalInflationPair, blocksPerYear uint64) { - if cache.isRefreshRequired(blocksPerYear) { - cache.refresh(inflations, blocksPerYear) - } -} - -// NOTE(pb): *NOT* thread safe -func (cache *MunicipalInflationCache) isRefreshRequired(blocksPerYear uint64) bool { +func (cache *MunicipalInflationCacheInternal) isRefreshRequired(blocksPerYear uint64) bool { return cache.blocksPerYear != blocksPerYear } diff --git a/x/mint/genesis.go b/x/mint/genesis.go index be7ac61d50..6a1eb3050d 100644 --- a/x/mint/genesis.go +++ b/x/mint/genesis.go @@ -2,12 +2,14 @@ package mint import ( sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/mint/cache" "github.com/cosmos/cosmos-sdk/x/mint/keeper" "github.com/cosmos/cosmos-sdk/x/mint/types" ) // InitGenesis new mint genesis func InitGenesis(ctx sdk.Context, keeper keeper.Keeper, ak types.AccountKeeper, data *types.GenesisState) { + cache.GMunicipalInflationCache.Refresh(&data.Minter.MunicipalInflation, data.Params.BlocksPerYear) keeper.SetMinter(ctx, data.Minter) keeper.SetParams(ctx, data.Params) ak.GetModuleAccount(ctx, types.ModuleName) diff --git a/x/mint/simulation/genesis.go b/x/mint/simulation/genesis.go index 783cc1d886..b0692f6122 100644 --- a/x/mint/simulation/genesis.go +++ b/x/mint/simulation/genesis.go @@ -5,9 +5,10 @@ package simulation import ( "encoding/json" "fmt" - "github.com/cosmos/cosmos-sdk/x/bank/simulation" "math/rand" + "github.com/cosmos/cosmos-sdk/x/bank/simulation" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" "github.com/cosmos/cosmos-sdk/x/mint/types" @@ -33,9 +34,9 @@ func GenInflationRate(r *rand.Rand) sdk.Dec { func GenMunicipalInflation(simState *module.SimulationState) []*types.MunicipalInflationPair { r := simState.Rand - coins := make([]*sdk.Coin, len(simulation.AdditionalTestSupply)) - for i := 0; i < len(simulation.AdditionalTestSupply); i++ { - coins[i] = &simulation.AdditionalTestSupply[i] + coins := make([]*sdk.Coin, len(simulation.AdditionalTestBalancePerAccount)) + for i := 0; i < len(simulation.AdditionalTestBalancePerAccount); i++ { + coins[i] = &simulation.AdditionalTestBalancePerAccount[i] } len_ := r.Intn(len(coins) + 1) @@ -44,7 +45,8 @@ func GenMunicipalInflation(simState *module.SimulationState) []*types.MunicipalI lenCoins := len(coins) lastIdx := lenCoins - 1 rndIdx := r.Intn(lenCoins) - fmt.Println(">>>>>>>>>>>>>>>", coins, "rndIdx:", rndIdx) + + // Swapping rndIdx element with the last element in the slice and cuttig slice without last element c := coins[rndIdx] coins[rndIdx] = coins[lastIdx] coins = coins[:lastIdx]