github.com/cosmos/cosmos-sdk@v0.50.10/docs/spec/store/README.md (about) 1 # Store 2 3 The store package defines the interfaces, types and abstractions for Cosmos SDK 4 modules to read and write to Merkleized state within a Cosmos SDK application. 5 The store package provides many primitives for developers to use in order to 6 work with both state storage and state commitment. Below we describe the various 7 abstractions. 8 9 ## Types 10 11 ### `Store` 12 13 The bulk of the store interfaces are defined [here](https://github.com/cosmos/cosmos-sdk/blob/main/store/types/store.go), 14 where the base primitive interface, for which other interfaces build off of, is 15 the `Store` type. The `Store` interface defines the ability to tell the type of 16 the implementing store and the ability to cache wrap via the `CacheWrapper` interface. 17 18 ### `CacheWrapper` & `CacheWrap` 19 20 One of the most important features a store has the ability to perform is the 21 ability to cache wrap. Cache wrapping is essentially the underlying store wrapping 22 itself within another store type that performs caching for both reads and writes 23 with the ability to flush writes via `Write()`. 24 25 ### `KVStore` & `CacheKVStore` 26 27 One of the most important interfaces that both developers and modules interface 28 with, which also provides the basis of most state storage and commitment operations, 29 is the `KVStore`. The `KVStore` interface provides basic CRUD abilities and 30 prefix-based iteration, including reverse iteration. 31 32 Typically, each module has it's own dedicated `KVStore` instance, which it can 33 get access to via the `sdk.Context` and the use of a pointer-based named key -- 34 `KVStoreKey`. The `KVStoreKey` provides pseudo-OCAP. How a exactly a `KVStoreKey` 35 maps to a `KVStore` will be illustrated below through the `CommitMultiStore`. 36 37 Note, a `KVStore` cannot directly commit state. Instead, a `KVStore` can be wrapped 38 by a `CacheKVStore` which extends a `KVStore` and provides the ability for the 39 caller to execute `Write()` which commits state to the underlying state storage. 40 Note, this doesn't actually flush writes to disk as writes are held in memory 41 until `Commit()` is called on the `CommitMultiStore`. 42 43 ### `CommitMultiStore` 44 45 The `CommitMultiStore` interface exposes the the top-level interface that is used 46 to manage state commitment and storage by an SDK application and abstracts the 47 concept of multiple `KVStore`s which are used by multiple modules. Specifically, 48 it supports the following high-level primitives: 49 50 * Allows for a caller to retrieve a `KVStore` by providing a `KVStoreKey`. 51 * Exposes pruning mechanisms to remove state pinned against a specific height/version 52 in the past. 53 * Allows for loading state storage at a particular height/version in the past to 54 provide current head and historical queries. 55 * Provides the ability to rollback state to a previous height/version. 56 * Provides the ability to to load state storage at a particular height/version 57 while also performing store upgrades, which are used during live hard-fork 58 application state migrations. 59 * Provides the ability to commit all current accumulated state to disk and performs 60 Merkle commitment. 61 62 ## Implementation Details 63 64 While there are many interfaces that the `store` package provides, there is 65 typically a core implementation for each main interface that modules and 66 developers interact with that are defined in the Cosmos SDK. 67 68 ### `iavl.Store` 69 70 The `iavl.Store` provides the core implementation for state storage and commitment 71 by implementing the following interfaces: 72 73 * `KVStore` 74 * `CommitStore` 75 * `CommitKVStore` 76 * `Queryable` 77 * `StoreWithInitialVersion` 78 79 It allows for all CRUD operations to be performed along with allowing current 80 and historical state queries, prefix iteration, and state commitment along with 81 Merkle proof operations. The `iavl.Store` also provides the ability to remove 82 historical state from the state commitment layer. 83 84 An overview of the IAVL implementation can be found [here](https://github.com/cosmos/iavl/blob/master/docs/overview.md). 85 It is important to note that the IAVL store provides both state commitment and 86 logical storage operations, which comes with drawbacks as there are various 87 performance impacts, some of which are very drastic, when it comes to the 88 operations mentioned above. 89 90 When dealing with state management in modules and clients, the Cosmos SDK provides 91 various layers of abstractions or "store wrapping", where the `iavl.Store` is the 92 bottom most layer. When requesting a store to perform reads or writes in a module, 93 the typical abstraction layer in order is defined as follows: 94 95 ```text 96 iavl.Store <- cachekv.Store <- gaskv.Store <- cachemulti.Store <- rootmulti.Store 97 ``` 98 99 ### Concurrent use of IAVL store 100 101 The tree under `iavl.Store` is not safe for concurrent use. It is the 102 responsibility of the caller to ensure that concurrent access to the store is 103 not performed. 104 105 The main issue with concurrent use is when data is written at the same time as 106 it's being iterated over. Doing so will cause a irrecoverable fatal error because 107 of concurrent reads and writes to an internal map. 108 109 Although it's not recommended, you can iterate through values while writing to 110 it by disabling "FastNode" **without guarantees that the values being written will 111 be returned during the iteration** (if you need this, you might want to reconsider 112 the design of your application). This is done by setting `iavl-disable-fastnode` 113 to `true` in the config TOML file. 114 115 ### `cachekv.Store` 116 117 The `cachekv.Store` store wraps an underlying `KVStore`, typically a `iavl.Store` 118 and contains an in-memory cache for storing pending writes to underlying `KVStore`. 119 `Set` and `Delete` calls are executed on the in-memory cache, whereas `Has` calls 120 are proxied to the underlying `KVStore`. 121 122 One of the most important calls to a `cachekv.Store` is `Write()`, which ensures 123 that key-value pairs are written to the underlying `KVStore` in a deterministic 124 and ordered manner by sorting the keys first. The store keeps track of "dirty" 125 keys and uses these to determine what keys to sort. In addition, it also keeps 126 track of deleted keys and ensures these are also removed from the underlying 127 `KVStore`. 128 129 The `cachekv.Store` also provides the ability to perform iteration and reverse 130 iteration. Iteration is performed through the `cacheMergeIterator` type and uses 131 both the dirty cache and underlying `KVStore` to iterate over key-value pairs. 132 133 Note, all calls to CRUD and iteration operations on a `cachekv.Store` are thread-safe. 134 135 ### `gaskv.Store` 136 137 The `gaskv.Store` store provides a simple implementation of a `KVStore`. 138 Specifically, it just wraps an existing `KVStore`, such as a cache-wrapped 139 `iavl.Store`, and incurs configurable gas costs for CRUD operations via 140 `ConsumeGas()` calls defined on the `GasMeter` which exists in a `sdk.Context` 141 and then proxies the underlying CRUD call to the underlying store. Note, the 142 `GasMeter` is reset on each block. 143 144 ### `cachemulti.Store` & `rootmulti.Store` 145 146 The `rootmulti.Store` acts as an abstraction around a series of stores. Namely, 147 it implements the `CommitMultiStore` an `Queryable` interfaces. Through the 148 `rootmulti.Store`, an SDK module can request access to a `KVStore` to perform 149 state CRUD operations and queries by holding access to a unique `KVStoreKey`. 150 151 The `rootmulti.Store` ensures these queries and state operations are performed 152 through cached-wrapped instances of `cachekv.Store` which is described above. The 153 `rootmulti.Store` implementation is also responsible for committing all accumulated 154 state from each `KVStore` to disk and returning an application state Merkle root. 155 156 Queries can be performed to return state data along with associated state 157 commitment proofs for both previous heights/versions and the current state root. 158 Queries are routed based on store name, i.e. a module, along with other parameters 159 which are defined in `abci.RequestQuery`. 160 161 The `rootmulti.Store` also provides primitives for pruning data at a given 162 height/version from state storage. When a height is committed, the `rootmulti.Store` 163 will determine if other previous heights should be considered for removal based 164 on the operator's pruning settings defined by `PruningOptions`, which defines 165 how many recent versions to keep on disk and the interval at which to remove 166 "staged" pruned heights from disk. During each interval, the staged heights are 167 removed from each `KVStore`. Note, it is up to the underlying `KVStore` 168 implementation to determine how pruning is actually performed. The `PruningOptions` 169 are defined as follows: 170 171 ```go 172 type PruningOptions struct { 173 // KeepRecent defines how many recent heights to keep on disk. 174 KeepRecent uint64 175 176 // Interval defines when the pruned heights are removed from disk. 177 Interval uint64 178 179 // Strategy defines the kind of pruning strategy. See below for more information on each. 180 Strategy PruningStrategy 181 } 182 ``` 183 184 The Cosmos SDK defines a preset number of pruning "strategies": `default`, `everything` 185 `nothing`, and `custom`. 186 187 It is important to note that the `rootmulti.Store` considers each `KVStore` as a 188 separate logical store. In other words, they do not share a Merkle tree or 189 comparable data structure. This means that when state is committed via 190 `rootmulti.Store`, each store is committed in sequence and thus is not atomic. 191 192 In terms of store construction and wiring, each Cosmos SDK application contains 193 a `BaseApp` instance which internally has a reference to a `CommitMultiStore` 194 that is implemented by a `rootmulti.Store`. The application then registers one or 195 more `KVStoreKey` that pertain to a unique module and thus a `KVStore`. Through 196 the use of an `sdk.Context` and a `KVStoreKey`, each module can get direct access 197 to it's respective `KVStore` instance. 198 199 Example: 200 201 ```go 202 func NewApp(...) Application { 203 // ... 204 205 bApp := baseapp.NewBaseApp(appName, logger, db, txConfig.TxDecoder(), baseAppOptions...) 206 bApp.SetCommitMultiStoreTracer(traceStore) 207 bApp.SetVersion(version.Version) 208 bApp.SetInterfaceRegistry(interfaceRegistry) 209 210 // ... 211 212 keys := sdk.NewKVStoreKeys(...) 213 transientKeys := sdk.NewTransientStoreKeys(...) 214 memKeys := sdk.NewMemoryStoreKeys(...) 215 216 // ... 217 218 // initialize stores 219 app.MountKVStores(keys) 220 app.MountTransientStores(transientKeys) 221 app.MountMemoryStores(memKeys) 222 223 // ... 224 } 225 ``` 226 227 The `rootmulti.Store` itself can be cache-wrapped which returns an instance of a 228 `cachemulti.Store`. For each block, `BaseApp` ensures that the proper abstractions 229 are created on the `CommitMultiStore`, i.e. ensuring that the `rootmulti.Store` 230 is cached-wrapped and uses the resulting `cachemulti.Store` to be set on the 231 `sdk.Context` which is then used for block and transaction execution. As a result, 232 all state mutations due to block and transaction execution are actually held 233 ephemerally until `Commit()` is called by the ABCI client. This concept is further 234 expanded upon when the AnteHandler is executed per transaction to ensure state 235 is not committed for transactions that failed CheckTx.