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.