From e01217cbe2faacc4a72a5b18a26198658bc646f5 Mon Sep 17 00:00:00 2001
From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com>
Date: Wed, 8 Dec 2021 11:01:40 +0100
Subject: [PATCH] feat: add configurable iavl cache size (backport #10561)
 (#10698)

* feat: add configurable iavl cache size (#10561)

<!--
The default pull request template is for types feat, fix, or refactor.
For other templates, add one of the following parameters to the url:
- template=docs.md
- template=other.md
-->

## Description

Closes: #1714

Bump default cache size to 50mb from 10kb. Allow node operators to set cache size.

---

### Author Checklist

*All items are required. Please add a note to the item if the item is not applicable and
please add links to any relevant follow up issues.*

I have...

- [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] added `!` to the type prefix if API or client breaking change
- [ ] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting))
- [ ] provided a link to the relevant issue or specification
- [ ] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules)
- [ ] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing)
- [ ] added a changelog entry to `CHANGELOG.md`
- [ ] included comments for [documenting Go code](https://blog.golang.org/godoc)
- [ ] updated the relevant documentation or specification
- [ ] reviewed "Files changed" and left comments if necessary
- [ ] confirmed all CI checks have passed

### Reviewers Checklist

*All items are required. Please add a note if the item is not applicable and please add
your handle next to the items reviewed if you only reviewed selected items.*

I have...

- [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] confirmed `!` in the type prefix if API or client breaking change
- [ ] confirmed all author checklist items have been addressed
- [ ] reviewed state machine logic
- [ ] reviewed API design and naming
- [ ] reviewed documentation is accurate
- [ ] reviewed tests and test coverage
- [ ] manually tested (if applicable)

(cherry picked from commit 76dde985364729222a53df33ffe14a0f60d46b7a)

# Conflicts:
#	CHANGELOG.md
#	store/rootmulti/store.go

* fix conflicts

* fix conflicts++

* Update CHANGELOG.md

Co-authored-by: Marko <marbar3778@yahoo.com>
Co-authored-by: Amaury <1293565+amaurym@users.noreply.github.com>
---
 CHANGELOG.md                  |  9 +++++++++
 baseapp/options.go            | 13 +++++++++----
 server/config/config.go       |  3 +++
 server/config/toml.go         |  4 ++++
 server/mock/store.go          |  3 +++
 store/iavl/store.go           | 10 +++++-----
 store/iavl/store_test.go      |  6 +++---
 store/rootmulti/proof_test.go |  2 +-
 store/rootmulti/store.go      | 24 +++++++++++++++---------
 store/types/iterator_test.go  |  2 +-
 store/types/store.go          |  3 +++
 11 files changed, 56 insertions(+), 23 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 099bd753706d..ab1383407081 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -46,8 +46,17 @@ Ref: https://keepachangelog.com/en/1.0.0/
 ## [v0.44.5](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.44.5) - 2021-12-02
 
 ### Improvements
+
 * (baseapp) [\#10631](https://github.com/cosmos/cosmos-sdk/pull/10631)  Emit ante events even for the failed txs. 
 
+### Features
+
+* [\#10561](https://github.com/cosmos/cosmos-sdk/pull/10561) Add configurable IAVL cache size to app.toml
+
+### API Breaking Changes
+
+* [\#10561](https://github.com/cosmos/cosmos-sdk/pull/10561) The `CommitMultiStore` interface contains a new `SetIAVLCacheSize` method
+
 ### Bug Fixes
 
 * [\#10648](https://github.com/cosmos/cosmos-sdk/pull/10648) Upgrade IAVL to 0.17.3 to solve race condition bug in IAVL.
diff --git a/baseapp/options.go b/baseapp/options.go
index be9fbdc659a0..5d336cb1bf15 100644
--- a/baseapp/options.go
+++ b/baseapp/options.go
@@ -17,7 +17,7 @@ import (
 
 // SetPruning sets a pruning option on the multistore associated with the app
 func SetPruning(opts sdk.PruningOptions) func(*BaseApp) {
-	return func(bap *BaseApp) { bap.cms.SetPruning(opts) }
+	return func(bapp *BaseApp) { bapp.cms.SetPruning(opts) }
 }
 
 // SetMinGasPrices returns an option that sets the minimum gas prices on the app.
@@ -27,17 +27,17 @@ func SetMinGasPrices(gasPricesStr string) func(*BaseApp) {
 		panic(fmt.Sprintf("invalid minimum gas prices: %v", err))
 	}
 
-	return func(bap *BaseApp) { bap.setMinGasPrices(gasPrices) }
+	return func(bapp *BaseApp) { bapp.setMinGasPrices(gasPrices) }
 }
 
 // SetHaltHeight returns a BaseApp option function that sets the halt block height.
 func SetHaltHeight(blockHeight uint64) func(*BaseApp) {
-	return func(bap *BaseApp) { bap.setHaltHeight(blockHeight) }
+	return func(bapp *BaseApp) { bapp.setHaltHeight(blockHeight) }
 }
 
 // SetHaltTime returns a BaseApp option function that sets the halt block time.
 func SetHaltTime(haltTime uint64) func(*BaseApp) {
-	return func(bap *BaseApp) { bap.setHaltTime(haltTime) }
+	return func(bapp *BaseApp) { bapp.setHaltTime(haltTime) }
 }
 
 // SetMinRetainBlocks returns a BaseApp option function that sets the minimum
@@ -57,6 +57,11 @@ func SetIndexEvents(ie []string) func(*BaseApp) {
 	return func(app *BaseApp) { app.setIndexEvents(ie) }
 }
 
+// SetIAVLCacheSize provides a BaseApp option function that sets the size of IAVL cache.
+func SetIAVLCacheSize(size int) func(*BaseApp) {
+	return func(bapp *BaseApp) { bapp.cms.SetIAVLCacheSize(size) }
+}
+
 // SetInterBlockCache provides a BaseApp option function that sets the
 // inter-block cache.
 func SetInterBlockCache(cache sdk.MultiStorePersistentCache) func(*BaseApp) {
diff --git a/server/config/config.go b/server/config/config.go
index b3d8a99cf6fe..f5a395dce8e0 100644
--- a/server/config/config.go
+++ b/server/config/config.go
@@ -69,6 +69,8 @@ type BaseConfig struct {
 	// IndexEvents defines the set of events in the form {eventType}.{attributeKey},
 	// which informs Tendermint what to index. If empty, all events will be indexed.
 	IndexEvents []string `mapstructure:"index-events"`
+	// IavlCacheSize set the size of the iavl tree cache.
+	IAVLCacheSize uint64 `mapstructure:"iavl-cache-size"`
 }
 
 // APIConfig defines the API listener configuration.
@@ -209,6 +211,7 @@ func DefaultConfig() *Config {
 			PruningInterval:   "0",
 			MinRetainBlocks:   0,
 			IndexEvents:       make([]string, 0),
+			IAVLCacheSize:     781250, // 50 MB
 		},
 		Telemetry: telemetry.Config{
 			Enabled:      false,
diff --git a/server/config/toml.go b/server/config/toml.go
index 9ecce83bcd17..9e8eeadaf1ef 100644
--- a/server/config/toml.go
+++ b/server/config/toml.go
@@ -70,6 +70,10 @@ inter-block-cache = {{ .BaseConfig.InterBlockCache }}
 # ["message.sender", "message.recipient"]
 index-events = {{ .BaseConfig.IndexEvents }}
 
+# IavlCacheSize set the size of the iavl tree cache. 
+# Default cache size is 50mb.
+iavl-cache-size = {{ .BaseConfig.IAVLCacheSize }}
+
 ###############################################################################
 ###                         Telemetry Configuration                         ###
 ###############################################################################
diff --git a/server/mock/store.go b/server/mock/store.go
index 33f573518c19..c8c233bb7c37 100644
--- a/server/mock/store.go
+++ b/server/mock/store.go
@@ -114,6 +114,9 @@ func (ms multiStore) GetStoreType() sdk.StoreType {
 func (ms multiStore) SetInterBlockCache(_ sdk.MultiStorePersistentCache) {
 	panic("not implemented")
 }
+func (ms multiStore) SetIAVLCacheSize(size int) {
+	panic("not implemented")
+}
 
 func (ms multiStore) SetInitialVersion(version int64) error {
 	panic("not implemented")
diff --git a/store/iavl/store.go b/store/iavl/store.go
index dcd3aed68c82..29a4d9871139 100644
--- a/store/iavl/store.go
+++ b/store/iavl/store.go
@@ -22,7 +22,7 @@ import (
 )
 
 const (
-	defaultIAVLCacheSize = 10000
+	DefaultIAVLCacheSize = 500000
 )
 
 var (
@@ -41,16 +41,16 @@ type Store struct {
 // LoadStore returns an IAVL Store as a CommitKVStore. Internally, it will load the
 // store's version (id) from the provided DB. An error is returned if the version
 // fails to load, or if called with a positive version on an empty tree.
-func LoadStore(db dbm.DB, id types.CommitID, lazyLoading bool) (types.CommitKVStore, error) {
-	return LoadStoreWithInitialVersion(db, id, lazyLoading, 0)
+func LoadStore(db dbm.DB, id types.CommitID, lazyLoading bool, cacheSize int) (types.CommitKVStore, error) {
+	return LoadStoreWithInitialVersion(db, id, lazyLoading, 0, cacheSize)
 }
 
 // LoadStoreWithInitialVersion returns an IAVL Store as a CommitKVStore setting its initialVersion
 // to the one given. Internally, it will load the store's version (id) from the
 // provided DB. An error is returned if the version fails to load, or if called with a positive
 // version on an empty tree.
-func LoadStoreWithInitialVersion(db dbm.DB, id types.CommitID, lazyLoading bool, initialVersion uint64) (types.CommitKVStore, error) {
-	tree, err := iavl.NewMutableTreeWithOpts(db, defaultIAVLCacheSize, &iavl.Options{InitialVersion: initialVersion})
+func LoadStoreWithInitialVersion(db dbm.DB, id types.CommitID, lazyLoading bool, initialVersion uint64, cacheSize int) (types.CommitKVStore, error) {
+	tree, err := iavl.NewMutableTreeWithOpts(db, cacheSize, &iavl.Options{InitialVersion: initialVersion})
 	if err != nil {
 		return nil, err
 	}
diff --git a/store/iavl/store_test.go b/store/iavl/store_test.go
index 26cf27db8bf0..b9c9e25ad43d 100644
--- a/store/iavl/store_test.go
+++ b/store/iavl/store_test.go
@@ -93,17 +93,17 @@ func TestLoadStore(t *testing.T) {
 	require.Equal(t, string(hcStore.Get([]byte("hello"))), "ciao")
 
 	// Querying a new store at some previous non-pruned height H
-	newHStore, err := LoadStore(db, cIDH, false)
+	newHStore, err := LoadStore(db, cIDH, false, DefaultIAVLCacheSize)
 	require.NoError(t, err)
 	require.Equal(t, string(newHStore.Get([]byte("hello"))), "hallo")
 
 	// Querying a new store at some previous pruned height Hp
-	newHpStore, err := LoadStore(db, cIDHp, false)
+	newHpStore, err := LoadStore(db, cIDHp, false, DefaultIAVLCacheSize)
 	require.NoError(t, err)
 	require.Equal(t, string(newHpStore.Get([]byte("hello"))), "hola")
 
 	// Querying a new store at current height H
-	newHcStore, err := LoadStore(db, cIDHc, false)
+	newHcStore, err := LoadStore(db, cIDHc, false, DefaultIAVLCacheSize)
 	require.NoError(t, err)
 	require.Equal(t, string(newHcStore.Get([]byte("hello"))), "ciao")
 }
diff --git a/store/rootmulti/proof_test.go b/store/rootmulti/proof_test.go
index f0bd29063ad1..10f8397e7284 100644
--- a/store/rootmulti/proof_test.go
+++ b/store/rootmulti/proof_test.go
@@ -14,7 +14,7 @@ import (
 func TestVerifyIAVLStoreQueryProof(t *testing.T) {
 	// Create main tree for testing.
 	db := dbm.NewMemDB()
-	iStore, err := iavl.LoadStore(db, types.CommitID{}, false)
+	iStore, err := iavl.LoadStore(db, types.CommitID{}, false, iavl.DefaultIAVLCacheSize)
 	store := iStore.(*iavl.Store)
 	require.Nil(t, err)
 	store.Set([]byte("MYKEY"), []byte("MYVALUE"))
diff --git a/store/rootmulti/store.go b/store/rootmulti/store.go
index 07dd864de1e3..17b9fd592bd3 100644
--- a/store/rootmulti/store.go
+++ b/store/rootmulti/store.go
@@ -48,6 +48,7 @@ type Store struct {
 	db             dbm.DB
 	lastCommitInfo *types.CommitInfo
 	pruningOpts    types.PruningOptions
+	iavlCacheSize  int
 	storesParams   map[types.StoreKey]storeParams
 	stores         map[types.StoreKey]types.CommitKVStore
 	keysByName     map[string]types.StoreKey
@@ -74,13 +75,14 @@ var (
 // LoadVersion must be called.
 func NewStore(db dbm.DB) *Store {
 	return &Store{
-		db:           db,
-		pruningOpts:  types.PruneNothing,
-		storesParams: make(map[types.StoreKey]storeParams),
-		stores:       make(map[types.StoreKey]types.CommitKVStore),
-		keysByName:   make(map[string]types.StoreKey),
-		pruneHeights: make([]int64, 0),
-		listeners:    make(map[types.StoreKey][]types.WriteListener),
+		db:            db,
+		pruningOpts:   types.PruneNothing,
+		iavlCacheSize: iavl.DefaultIAVLCacheSize,
+		storesParams:  make(map[types.StoreKey]storeParams),
+		stores:        make(map[types.StoreKey]types.CommitKVStore),
+		keysByName:    make(map[string]types.StoreKey),
+		pruneHeights:  make([]int64, 0),
+		listeners:     make(map[types.StoreKey][]types.WriteListener),
 	}
 }
 
@@ -96,6 +98,10 @@ func (rs *Store) SetPruning(pruningOpts types.PruningOptions) {
 	rs.pruningOpts = pruningOpts
 }
 
+func (rs *Store) SetIAVLCacheSize(cacheSize int) {
+	rs.iavlCacheSize = cacheSize
+}
+
 // SetLazyLoading sets if the iavl store should be loaded lazily or not
 func (rs *Store) SetLazyLoading(lazyLoading bool) {
 	rs.lazyLoading = lazyLoading
@@ -876,9 +882,9 @@ func (rs *Store) loadCommitStoreFromParams(key types.StoreKey, id types.CommitID
 		var err error
 
 		if params.initialVersion == 0 {
-			store, err = iavl.LoadStore(db, id, rs.lazyLoading)
+			store, err = iavl.LoadStore(db, id, rs.lazyLoading, rs.iavlCacheSize)
 		} else {
-			store, err = iavl.LoadStoreWithInitialVersion(db, id, rs.lazyLoading, params.initialVersion)
+			store, err = iavl.LoadStoreWithInitialVersion(db, id, rs.lazyLoading, params.initialVersion, rs.iavlCacheSize)
 		}
 
 		if err != nil {
diff --git a/store/types/iterator_test.go b/store/types/iterator_test.go
index 3086917b605b..686aa1123708 100644
--- a/store/types/iterator_test.go
+++ b/store/types/iterator_test.go
@@ -12,7 +12,7 @@ import (
 
 func newMemTestKVStore(t *testing.T) types.KVStore {
 	db := dbm.NewMemDB()
-	store, err := iavl.LoadStore(db, types.CommitID{}, false)
+	store, err := iavl.LoadStore(db, types.CommitID{}, false, iavl.DefaultIAVLCacheSize)
 	require.NoError(t, err)
 	return store
 }
diff --git a/store/types/store.go b/store/types/store.go
index 630cd1d040b9..1bad58ea2eba 100644
--- a/store/types/store.go
+++ b/store/types/store.go
@@ -188,6 +188,9 @@ type CommitMultiStore interface {
 	// SetInitialVersion sets the initial version of the IAVL tree. It is used when
 	// starting a new chain at an arbitrary height.
 	SetInitialVersion(version int64) error
+
+	// SetIAVLCacheSize sets the cache size of the IAVL tree.
+	SetIAVLCacheSize(size int)
 }
 
 //---------subsp-------------------------------