diff --git a/baseapp/abci.go b/baseapp/abci.go index aaa48bb8c0..36c36d1e11 100644 --- a/baseapp/abci.go +++ b/baseapp/abci.go @@ -286,7 +286,7 @@ func (app *BaseApp) Commit() (res abci.ResponseCommit) { header := app.deliverState.ctx.BlockHeader() retainHeight := app.GetBlockRetentionHeight(header.Height) - // Write the DeliverTx state which is cache-wrapped and commit the MultiStore. + // Write the DeliverTx state into branched storage and commit the MultiStore. // The write to the DeliverTx state writes all state transitions to the root // MultiStore (app.cms) so when Commit() is called is persists those values. app.deliverState.ms.Write() @@ -629,7 +629,7 @@ func (app *BaseApp) createQueryContext(height int64, prove bool) (sdk.Context, e ) } - // cache wrap the commit-multistore for safety + // branch the commit-multistore for safety ctx := sdk.NewContext( cacheMS, app.checkState.ctx.BlockHeader(), true, app.logger, ).WithMinGasPrices(app.minGasPrices) diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index ebd67408c9..a79aed801b 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -357,8 +357,8 @@ func (app *BaseApp) Seal() { app.sealed = true } // IsSealed returns true if the BaseApp is sealed and false otherwise. func (app *BaseApp) IsSealed() bool { return app.sealed } -// setCheckState sets the BaseApp's checkState with a cache-wrapped multi-store -// (i.e. a CacheMultiStore) and a new Context with the cache-wrapped multi-store, +// setCheckState sets the BaseApp's checkState with a branched multi-store +// (i.e. a CacheMultiStore) and a new Context with the same multi-store branch, // provided header, and minimum gas prices set. It is set on InitChain and reset // on Commit. func (app *BaseApp) setCheckState(header tmproto.Header) { @@ -369,8 +369,8 @@ func (app *BaseApp) setCheckState(header tmproto.Header) { } } -// setDeliverState sets the BaseApp's deliverState with a cache-wrapped multi-store -// (i.e. a CacheMultiStore) and a new Context with the cache-wrapped multi-store, +// setDeliverState sets the BaseApp's deliverState with a branched multi-store +// (i.e. a CacheMultiStore) and a new Context with the same multi-store branch, // and provided header. It is set on InitChain and BeginBlock and set to nil on // Commit. func (app *BaseApp) setDeliverState(header tmproto.Header) { @@ -532,7 +532,7 @@ func (app *BaseApp) getContextForTx(mode runTxMode, txBytes []byte) sdk.Context } // cacheTxContext returns a new context based off of the provided context with -// a cache wrapped multi-store. +// a branched multi-store. func (app *BaseApp) cacheTxContext(ctx sdk.Context, txBytes []byte) (sdk.Context, sdk.CacheMultiStore) { ms := ctx.MultiStore() // TODO: https://github.com/cosmos/cosmos-sdk/issues/2824 @@ -620,7 +620,7 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte) (gInfo sdk.GasInfo, re msCache sdk.CacheMultiStore ) - // Cache wrap context before AnteHandler call in case it aborts. + // Branch context before AnteHandler call in case it aborts. // This is required for both CheckTx and DeliverTx. // Ref: https://github.com/cosmos/cosmos-sdk/issues/2772 // @@ -632,9 +632,8 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte) (gInfo sdk.GasInfo, re newCtx, err := app.anteHandler(anteCtx, tx, mode == runTxModeSimulate) if !newCtx.IsZero() { - // At this point, newCtx.MultiStore() is cache-wrapped, or something else - // replaced by the AnteHandler. We want the original multistore, not one - // which was cache-wrapped for the AnteHandler. + // At this point, newCtx.MultiStore() is a store branch, or something else + // replaced by the AnteHandler. We want the original multistore. // // Also, in the case of the tx aborting, we need to track gas consumed via // the instantiated gas meter in the AnteHandler, so we update the context @@ -654,9 +653,9 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte) (gInfo sdk.GasInfo, re msCache.Write() } - // Create a new Context based off of the existing Context with a cache-wrapped - // MultiStore in case message processing fails. At this point, the MultiStore - // is doubly cached-wrapped. + // Create a new Context based off of the existing Context with a MultiStore branch + // in case message processing fails. At this point, the MultiStore + // is a branch of a branch. runMsgCtx, msCache := app.cacheTxContext(ctx, txBytes) // Attempt to execute all messages and only update state if all messages pass diff --git a/docs/building-modules/msg-services.md b/docs/building-modules/msg-services.md index 0df12fa911..868d74b4a2 100644 --- a/docs/building-modules/msg-services.md +++ b/docs/building-modules/msg-services.md @@ -67,7 +67,7 @@ Here is the typical structure of a `handler` function: Let us break it down: - The [`Msg`](./messages-and-queries.md#messages) is the actual object being processed. -- The [`Context`](../core/context.md) contains all the necessary information needed to process the `msg`, as well as a cache-wrapped copy of the latest state. If the `msg` is succesfully processed, the modified version of the temporary state contained in the `ctx` will be written to the main state. +- The [`Context`](../core/context.md) contains all the necessary information needed to process the `msg`, as well as a branch of the latest state. If the `msg` is successfully processed, the branched version of the state contained in the `ctx` will be written to the main state (branch). - The [`*Result`] returned to `BaseApp` contains (among other things) information on the execution of the `handler` and [events](../core/events.md). Module `handler`s are typically implemented in a `./handler.go` file inside the module's folder. The [module manager](./module-manager.md) is used to add the module's `handler`s to the diff --git a/docs/building-modules/query-services.md b/docs/building-modules/query-services.md index fb64a2e1aa..e9f7f8c77d 100644 --- a/docs/building-modules/query-services.md +++ b/docs/building-modules/query-services.md @@ -21,7 +21,7 @@ Let us break it down: - The `path` is an array of `string`s that contains the type of the query, and that can also contain `query` arguments. See [`queries`](./messages-and-queries.md#queries) for more information. - The `req` itself is primarily used to retrieve arguments if they are too large to fit in the `path`. This is done using the `Data` field of `req`. -- The [`Context`](../core/context.md) contains all the necessary information needed to process the `query`, as well as a cache-wrapped copy of the latest state. It is primarily used by the [`keeper`](./keeper.md) to access the state. +- The [`Context`](../core/context.md) contains all the necessary information needed to process the `query`, as well as a branch of the latest state. It is primarily used by the [`keeper`](./keeper.md) to access the state. - The result `res` returned to `BaseApp`, marshalled using the application's [`codec`](../core/encoding.md). ## Implementation of a module query service diff --git a/docs/core/baseapp.md b/docs/core/baseapp.md index 421ed74c93..8e36a7363d 100644 --- a/docs/core/baseapp.md +++ b/docs/core/baseapp.md @@ -126,15 +126,16 @@ is the canonical state of the application and the volatile states, `checkState` are used to handle state transitions in-between the main state made during [`Commit`](#commit). Internally, there is only a single `CommitMultiStore` which we refer to as the main or root state. -From this root state, we derive two volatile state through a mechanism called cache-wrapping. The -types can be illustrated as follows: +From this root state, we derive two volatile state through a mechanism called _store branching_ (performed by `CacheWrap` function). +The types can be illustrated as follows: ![Types](./baseapp_state_types.png) ### InitChain State Updates -During `InitChain`, the two volatile states, `checkState` and `deliverState` are set by cache-wrapping -the root `CommitMultiStore`. Any subsequent reads and writes happen on cached versions of the `CommitMultiStore`. +During `InitChain`, the two volatile states, `checkState` and `deliverState` are set by branching +the root `CommitMultiStore`. Any subsequent reads and writes happen on branched versions of the `CommitMultiStore`. +To avoid unnecessary roundtrip to the main state, all reads to the branched store are cached. ![InitChain](./baseapp_state-initchain.png) @@ -142,8 +143,8 @@ the root `CommitMultiStore`. Any subsequent reads and writes happen on cached ve During `CheckTx`, the `checkState`, which is based off of the last committed state from the root store, is used for any reads and writes. Here we only execute the `AnteHandler` and verify a service router -exists for every message in the transaction. Note, when we execute the `AnteHandler`, we cache-wrap -the already cache-wrapped `checkState`. This has the side effect that if the `AnteHandler` fails, +exists for every message in the transaction. Note, when we execute the `AnteHandler`, we branch +the already branched `checkState`. This has the side effect that if the `AnteHandler` fails, the state transitions won't be reflected in the `checkState` -- i.e. `checkState` is only updated on success. @@ -152,7 +153,7 @@ success. ### BeginBlock State Updates During `BeginBlock`, the `deliverState` is set for use in subsequent `DeliverTx` ABCI messages. The -`deliverState` is based off of the last committed state from the root store and is cache-wrapped. +`deliverState` is based off of the last committed state from the root store and is branched. Note, the `deliverState` is set to `nil` on [`Commit`](#commit). ![BeginBlock](./baseapp_state-begin_block.png) @@ -161,7 +162,7 @@ Note, the `deliverState` is set to `nil` on [`Commit`](#commit). The state flow for `DeliverTx` is nearly identical to `CheckTx` except state transitions occur on the `deliverState` and messages in a transaction are executed. Similarly to `CheckTx`, state transitions -occur on a doubly cache-wrapped state -- `deliverState`. Successful message execution results in +occur on a doubly branched state -- `deliverState`. Successful message execution results in writes being committed to `deliverState`. Note, if message execution fails, state transitions from the AnteHandler are persisted. @@ -283,7 +284,7 @@ Before the first transaction of a given block is processed, a [volatile state](# `DeliverTx` performs the **exact same steps as `CheckTx`**, with a little caveat at step 3 and the addition of a fifth step: 1. The `AnteHandler` does **not** check that the transaction's `gas-prices` is sufficient. That is because the `min-gas-prices` value `gas-prices` is checked against is local to the node, and therefore what is enough for one full-node might not be for another. This means that the proposer can potentially include transactions for free, although they are not incentivised to do so, as they earn a bonus on the total fee of the block they propose. -2. For each `Msg` in the transaction, route to the appropriate module's [`Msg` service](../building-modules/msg-services.md). Additional _stateful_ checks are performed, and the cache-wrapped multistore held in `deliverState`'s `context` is updated by the module's `keeper`. If the `Msg` service returns successfully, the cache-wrapped multistore held in `context` is written to `deliverState` `CacheMultiStore`. +2. For each `Msg` in the transaction, route to the appropriate module's [`Msg` service](../building-modules/msg-services.md). Additional _stateful_ checks are performed, and the branched multistore held in `deliverState`'s `context` is updated by the module's `keeper`. If the `Msg` service returns successfully, the branched multistore held in `context` is written to `deliverState` `CacheMultiStore`. During step 5., each read/write to the store increases the value of `GasConsumed`. You can find the default cost of each operation: @@ -308,17 +309,17 @@ At any point, if `GasConsumed > GasWanted`, the function returns with `Code != 0 `RunTx` is called from `CheckTx`/`DeliverTx` to handle the transaction, with `runTxModeCheck` or `runTxModeDeliver` as parameter to differentiate between the two modes of execution. Note that when `RunTx` receives a transaction, it has already been decoded. -The first thing `RunTx` does upon being called is to retrieve the `context`'s `CacheMultiStore` by calling the `getContextForTx()` function with the appropriate mode (either `runTxModeCheck` or `runTxModeDeliver`). This `CacheMultiStore` is a cached version of the main store instantiated during `BeginBlock` for `DeliverTx` and during the `Commit` of the previous block for `CheckTx`. After that, two `defer func()` are called for [`gas`](../basics/gas-fees.md) management. They are executed when `runTx` returns and make sure `gas` is actually consumed, and will throw errors, if any. +The first thing `RunTx` does upon being called is to retrieve the `context`'s `CacheMultiStore` by calling the `getContextForTx()` function with the appropriate mode (either `runTxModeCheck` or `runTxModeDeliver`). This `CacheMultiStore` is a branch of the main store, with cache functionality (for query requests), instantiated during `BeginBlock` for `DeliverTx` and during the `Commit` of the previous block for `CheckTx`. After that, two `defer func()` are called for [`gas`](../basics/gas-fees.md) management. They are executed when `runTx` returns and make sure `gas` is actually consumed, and will throw errors, if any. After that, `RunTx()` calls `ValidateBasic()` on each `Msg`in the `Tx`, which runs preliminary _stateless_ validity checks. If any `Msg` fails to pass `ValidateBasic()`, `RunTx()` returns with an error. -Then, the [`anteHandler`](#antehandler) of the application is run (if it exists). In preparation of this step, both the `checkState`/`deliverState`'s `context` and `context`'s `CacheMultiStore` are cached-wrapped using the `cacheTxContext()` function. +Then, the [`anteHandler`](#antehandler) of the application is run (if it exists). In preparation of this step, both the `checkState`/`deliverState`'s `context` and `context`'s `CacheMultiStore` are branched using the `cacheTxContext()` function. +++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/baseapp/baseapp.go#L623-L630 This allows `RunTx` not to commit the changes made to the state during the execution of `anteHandler` if it ends up failing. It also prevents the module implementing the `anteHandler` from writing to state, which is an important part of the [object-capabilities](./ocap.md) of the Cosmos SDK. -Finally, the [`RunMsgs()`](#runmsgs) function is called to process the `Msg`s in the `Tx`. In preparation of this step, just like with the `anteHandler`, both the `checkState`/`deliverState`'s `context` and `context`'s `CacheMultiStore` are cached-wrapped using the `cacheTxContext()` function. +Finally, the [`RunMsgs()`](#runmsgs) function is called to process the `Msg`s in the `Tx`. In preparation of this step, just like with the `anteHandler`, both the `checkState`/`deliverState`'s `context` and `context`'s `CacheMultiStore` are branched using the `cacheTxContext()` function. ### AnteHandler @@ -373,7 +374,7 @@ The [`EndBlock` ABCI message](#https://tendermint.com/docs/app-dev/abci-spec.htm The [`Commit` ABCI message](https://tendermint.com/docs/app-dev/abci-spec.html#commit) is sent from the underlying Tendermint engine after the full-node has received _precommits_ from 2/3+ of validators (weighted by voting power). On the `BaseApp` end, the `Commit(res abci.ResponseCommit)` function is implemented to commit all the valid state transitions that occured during `BeginBlock`, `DeliverTx` and `EndBlock` and to reset state for the next block. -To commit state-transitions, the `Commit` function calls the `Write()` function on `deliverState.ms`, where `deliverState.ms` is a cached multistore of the main store `app.cms`. Then, the `Commit` function sets `checkState` to the latest header (obtbained from `deliverState.ctx.BlockHeader`) and `deliverState` to `nil`. +To commit state-transitions, the `Commit` function calls the `Write()` function on `deliverState.ms`, where `deliverState.ms` is a branched multistore of the main store `app.cms`. Then, the `Commit` function sets `checkState` to the latest header (obtbained from `deliverState.ctx.BlockHeader`) and `deliverState` to `nil`. Finally, `Commit` returns the hash of the commitment of `app.cms` back to the underlying consensus engine. This hash is used as a reference in the header of the next block. @@ -390,7 +391,7 @@ Each Tendermint `query` comes with a `path`, which is a `string` which denotes w - Application-related queries like querying the application's version, which are served via the `handleQueryApp` method. - Direct queries to the multistore, which are served by the `handlerQueryStore` method. These direct queryeis are different from custom queries which go through `app.queryRouter`, and are mainly used by third-party service provider like block explorers. - P2P queries, which are served via the `handleQueryP2P` method. These queries return either `app.addrPeerFilter` or `app.ipPeerFilter` that contain the list of peers filtered by address or IP respectively. These lists are first initialized via `options` in `BaseApp`'s [constructor](#constructor). -- Custom queries, which encompass legacy queries (before the introduction of gRPC queries), are served via the `handleQueryCustom` method. The `handleQueryCustom` cache-wraps the multistore before using the `queryRoute` obtained from `app.queryRouter` to map the query to the appropriate module's [legacy `querier`](../building-modules/query-services.md#legacy-queriers). +- Custom queries, which encompass legacy queries (before the introduction of gRPC queries), are served via the `handleQueryCustom` method. The `handleQueryCustom` branches the multistore before using the `queryRoute` obtained from `app.queryRouter` to map the query to the appropriate module's [legacy `querier`](../building-modules/query-services.md#legacy-queriers). ## Next {hide} diff --git a/docs/core/context.md b/docs/core/context.md index cc0628b5eb..c9e89b16a4 100644 --- a/docs/core/context.md +++ b/docs/core/context.md @@ -4,7 +4,7 @@ order: 3 # Context -The `context` is a data structure intended to be passed from function to function that carries information about the current state of the application. It holds a cached copy of the entire state as well as useful objects and information like `gasMeter`, `block height`, `consensus parameters` and more. {synopsis} +The `context` is a data structure intended to be passed from function to function that carries information about the current state of the application. It provides an access to a branched storage (a safe branch of the entire state) as well as useful objects and information like `gasMeter`, `block height`, `consensus parameters` and more. {synopsis} ## Pre-requisites Readings @@ -54,21 +54,23 @@ childCtx = parentCtx.WithBlockHeader(header) The [Golang Context Package](https://golang.org/pkg/context) documentation instructs developers to explicitly pass a context `ctx` as the first argument of a process. -## Cache Wrapping +## Store branching -The `Context` contains a `MultiStore`, which allows for cache-wrapping functionality: a `CacheMultiStore` -where each `KVStore` is is wrapped with an ephemeral cache. Processes are free to write changes to -the `CacheMultiStore`, then write the changes back to the original state or disregard them if something +The `Context` contains a `MultiStore`, which allows for branchinig and caching functionality using `CacheMultiStore` +(queries in `CacheMultiStore` are cached to avoid future round trips). +Each `KVStore` is branched in a safe and isolated ephemeral storage. Processes are free to write changes to +the `CacheMultiStore`. If a state-transition sequence is performed without issue, the store branch can +be committed to the underlying store at the end of the sequence or disregard them if something goes wrong. The pattern of usage for a Context is as follows: 1. A process receives a Context `ctx` from its parent process, which provides information needed to perform the process. -2. The `ctx.ms` is **cache wrapped**, i.e. a cached copy of the [multistore](./store.md#multistore) is made so that the process can make changes to the state as it executes, without changing the original`ctx.ms`. This is useful to protect the underlying multistore in case the changes need to be reverted at some point in the execution. +2. The `ctx.ms` is a **branched store**, i.e. a branch of the [multistore](./store.md#multistore) is made so that the process can make changes to the state as it executes, without changing the original`ctx.ms`. This is useful to protect the underlying multistore in case the changes need to be reverted at some point in the execution. 3. The process may read and write from `ctx` as it is executing. It may call a subprocess and pass `ctx` to it as needed. 4. When a subprocess returns, it checks if the result is a success or failure. If a failure, nothing - needs to be done - the cache wrapped `ctx` is simply discarded. If successful, the changes made to - the cache-wrapped `MultiStore` can be committed to the original `ctx.ms` via `Write()`. + needs to be done - the branch `ctx` is simply discarded. If successful, the changes made to + the `CacheMultiStore` can be committed to the original `ctx.ms` via `Write()`. For example, here is a snippet from the [`runTx`](./baseapp.md#runtx-and-runmsgs) function in [`baseapp`](./baseapp.md): @@ -90,12 +92,12 @@ if result.IsOK() { Here is the process: 1. Prior to calling `runMsgs` on the message(s) in the transaction, it uses `app.cacheTxContext()` - to cache-wrap the context and multistore. -2. The cache-wrapped context, `runMsgCtx`, is used in `runMsgs` to return a result. + to branch and cache the context and multistore. +2. `runMsgCtx` - the context with branched store, is used in `runMsgs` to return a result. 3. If the process is running in [`checkTxMode`](./baseapp.md#checktx), there is no need to write the changes - the result is returned immediately. 4. If the process is running in [`deliverTxMode`](./baseapp.md#delivertx) and the result indicates - a successful run over all the messages, the cached multistore is written back to the original. + a successful run over all the messages, the branched multistore is written back to the original. ## Next {hide} diff --git a/docs/core/store.md b/docs/core/store.md index b9e97163ba..7663c705d2 100644 --- a/docs/core/store.md +++ b/docs/core/store.md @@ -56,15 +56,15 @@ The Cosmos SDK comes with a large set of stores to persist the state of applicat ### Store Interface -At its very core, a Cosmos SDK `store` is an object that holds a `CacheWrapper` and implements a `GetStoreType()` method: +At its very core, a Cosmos SDK `store` is an object that holds a `CacheWrapper` and has a `GetStoreType()` method: +++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/store/types/store.go#L15-L18 -The `GetStoreType` is a simple method that returns the type of store, whereas a `CacheWrapper` is a simple interface that specifies cache-wrapping and `Write` methods: +The `GetStoreType` is a simple method that returns the type of store, whereas a `CacheWrapper` is a simple interface that implements store read caching and write branching through `Write` method: +++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/store/types/store.go#L240-L264 -Cache-wrapping is used ubiquitously in the Cosmos SDK and required to be implemented on every store type. A cache-wrapper creates a light snapshot of a store that can be passed around and updated without affecting the main underlying store. This is used to trigger temporary state-transitions that may be reverted later should an error occur. If a state-transition sequence is performed without issue, the cached store can be committed to the underlying store at the end of the sequence. +Branching and cache is used ubiquitously in the Cosmos SDK and required to be implemented on every store type. A storage branch creates an isolated, ephemeral branch of a store that can be passed around and updated without affecting the main underlying store. This is used to trigger temporary state-transitions that may be reverted later should an error occur. Read more about it in [context](./context.md#Store-branching) ### Commit Store @@ -88,7 +88,7 @@ Each Cosmos SDK application holds a multistore at its root to persist its state. +++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/store/types/store.go#L104-L133 -If tracing is enabled, then cache-wrapping the multistore will wrap all the underlying `KVStore` in [`TraceKv.Store`](#tracekv-store) before caching them. +If tracing is enabled, then branching the multistore will firstly wrap all the underlying `KVStore` in [`TraceKv.Store`](#tracekv-store). ### CommitMultiStore @@ -104,11 +104,11 @@ The `rootMulti.Store` is a base-layer multistore built around a `db` on top of w ### CacheMultiStore -Whenever the `rootMulti.Store` needs to be cached-wrapped, a [`cachemulti.Store`](https://github.com/cosmos/cosmos-sdk/blob/master/store/cachemulti/store.go) is used. +Whenever the `rootMulti.Store` needs to be branched, a [`cachemulti.Store`](https://github.com/cosmos/cosmos-sdk/blob/master/store/cachemulti/store.go) is used. +++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/store/cachemulti/store.go#L17-L28 -`cachemulti.Store` cache wraps all substores in its constructor and hold them in `Store.stores`. `Store.GetKVStore()` returns the store from `Store.stores`, and `Store.Write()` recursively calls `CacheWrap.Write()` on all the substores. +`cachemulti.Store` branches all substores (creates a virtual store for each substore) in its constructor and hold them in `Store.stores`. Moreover cachese all read queries. `Store.GetKVStore()` returns the store from `Store.stores`, and `Store.Write()` recursively calls `CacheWrap.Write()` on all the substores. ## Base-layer KVStores @@ -172,19 +172,19 @@ Transient stores are typically accessed via the [`context`](./context.md) via th +++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0-rc3/store/cachekv/store.go#L27-L34 -This is the type used whenever an IAVL Store needs to be cache-wrapped (typically when setting value that might be reverted later). +This is the type used whenever an IAVL Store needs to be branched to create an isolated store (typically when we need to mutate a state that might be reverted later). #### `Get` -`Store.Get()` checks `Store.cache` first in order to find if there is any cached value associated with the key. If the value exists, the function returns it. If not, the function calls `Store.parent.Get()`, sets the key-value pair to the `Store.cache`, and returns it. +`Store.Get()` firstly checks if `Store.cache` has an associated value with the key. If the value exists, the function returns it. If not, the function calls `Store.parent.Get()`, caches the result in `Store.cache`, and returns it. #### `Set` -`Store.Set()` sets the key-value pair to the `Store.cache`. `cValue` has the field dirty bool which indicates whether the cached value is different from the underlying value. When `Store.Set()` cache new pair, the `cValue.dirty` is set `true` so when `Store.Write()` is called it can be written to the underlying store. +`Store.Set()` sets the key-value pair to the `Store.cache`. `cValue` has the field dirty bool which indicates whether the cached value is different from the underlying value. When `Store.Set()` cachees a new pair, the `cValue.dirty` is set `true` so when `Store.Write()` is called it can be written to the underlying store. #### `Iterator` -`Store.Iterator()` have to traverse on both caches items and the original items. In `Store.iterator()`, two iterators are generated for each of them, and merged. `memIterator` is essentially a slice of the `KVPairs`, used for cached items. `mergeIterator` is a combination of two iterators, where traverse happens ordered on both iterators. +`Store.Iterator()` have to traverse on both cached items and the original items. In `Store.iterator()`, two iterators are generated for each of them, and merged. `memIterator` is essentially a slice of the `KVPairs`, used for cached items. `mergeIterator` is a combination of two iterators, where traverse happens ordered on both iterators. ### `GasKv` Store diff --git a/store/README.md b/store/README.md index 54994374bc..3607cbb71b 100644 --- a/store/README.md +++ b/store/README.md @@ -34,7 +34,7 @@ type Store struct { } ``` -`cachemulti.Store` cache wraps all substores in its constructor and hold them in `Store.stores`. `Store.GetKVStore()` returns the store from `Store.stores`, and `Store.Write()` recursively calls `CacheWrap.Write()` on the substores. +`cachemulti.Store` branches all substores in its constructor and hold them in `Store.stores`. `Store.GetKVStore()` returns the store from `Store.stores`, and `Store.Write()` recursively calls `CacheWrap.Write()` on the substores. ## DBAdapter diff --git a/store/cache/cache.go b/store/cache/cache.go index b58f0531fc..62bed91d97 100644 --- a/store/cache/cache.go +++ b/store/cache/cache.go @@ -89,7 +89,7 @@ func (cmgr *CommitKVStoreCacheManager) Reset() { } } -// CacheWrap returns the inter-block cache as a cache-wrapped CommitKVStore. +// CacheWrap implements the CacheWrapper interface func (ckv *CommitKVStoreCache) CacheWrap() types.CacheWrap { return cachekv.NewStore(ckv) } diff --git a/store/cachekv/store.go b/store/cachekv/store.go index dd8f95215b..b2e394e95f 100644 --- a/store/cachekv/store.go +++ b/store/cachekv/store.go @@ -35,6 +35,7 @@ type Store struct { var _ types.CacheKVStore = (*Store)(nil) +// NewStore creates a new Store object func NewStore(parent types.KVStore) *Store { return &Store{ cache: make(map[string]*cValue), @@ -44,12 +45,12 @@ func NewStore(parent types.KVStore) *Store { } } -// Implements Store. +// GetStoreType implements Store. func (store *Store) GetStoreType() types.StoreType { return store.parent.GetStoreType() } -// Implements types.KVStore. +// Get implements types.KVStore. func (store *Store) Get(key []byte) (value []byte) { store.mtx.Lock() defer store.mtx.Unlock() @@ -68,7 +69,7 @@ func (store *Store) Get(key []byte) (value []byte) { return value } -// Implements types.KVStore. +// Set implements types.KVStore. func (store *Store) Set(key []byte, value []byte) { store.mtx.Lock() defer store.mtx.Unlock() @@ -80,13 +81,13 @@ func (store *Store) Set(key []byte, value []byte) { store.setCacheValue(key, value, false, true) } -// Implements types.KVStore. +// Has implements types.KVStore. func (store *Store) Has(key []byte) bool { value := store.Get(key) return value != nil } -// Implements types.KVStore. +// Delete implements types.KVStore. func (store *Store) Delete(key []byte) { store.mtx.Lock() defer store.mtx.Unlock() @@ -135,10 +136,7 @@ func (store *Store) Write() { store.sortedCache = list.New() } -//---------------------------------------- -// To cache-wrap this Store further. - -// Implements CacheWrapper. +// CacheWrap implements CacheWrapper. func (store *Store) CacheWrap() types.CacheWrap { return NewStore(store) } @@ -151,12 +149,12 @@ func (store *Store) CacheWrapWithTrace(w io.Writer, tc types.TraceContext) types //---------------------------------------- // Iteration -// Implements types.KVStore. +// Iterator implements types.KVStore. func (store *Store) Iterator(start, end []byte) types.Iterator { return store.iterator(start, end, true) } -// Implements types.KVStore. +// ReverseIterator implements types.KVStore. func (store *Store) ReverseIterator(start, end []byte) types.Iterator { return store.iterator(start, end, false) } diff --git a/store/cachemulti/store.go b/store/cachemulti/store.go index 463781656b..59a29c358c 100644 --- a/store/cachemulti/store.go +++ b/store/cachemulti/store.go @@ -14,7 +14,7 @@ import ( //---------------------------------------- // Store -// Store holds many cache-wrapped stores. +// Store holds many branched stores. // Implements MultiStore. // NOTE: a Store (and MultiStores in general) should never expose the // keys for the substores. @@ -31,7 +31,7 @@ var _ types.CacheMultiStore = Store{} // NewFromKVStore creates a new Store object from a mapping of store keys to // CacheWrapper objects and a KVStore as the database. Each CacheWrapper store -// is cache-wrapped. +// is a branched store. func NewFromKVStore( store types.KVStore, stores map[types.StoreKey]types.CacheWrapper, keys map[string]types.StoreKey, traceWriter io.Writer, traceContext types.TraceContext, @@ -56,7 +56,7 @@ func NewFromKVStore( } // NewStore creates a new Store object from a mapping of store keys to -// CacheWrapper objects. Each CacheWrapper store is cache-wrapped. +// CacheWrapper objects. Each CacheWrapper store is a branched store. func NewStore( db dbm.DB, stores map[types.StoreKey]types.CacheWrapper, keys map[string]types.StoreKey, traceWriter io.Writer, traceContext types.TraceContext, @@ -136,7 +136,7 @@ func (cms Store) CacheMultiStore() types.CacheMultiStore { // TODO: The store implementation can possibly be modified to support this as it // seems safe to load previous versions (heights). func (cms Store) CacheMultiStoreWithVersion(_ int64) (types.CacheMultiStore, error) { - panic("cannot cache-wrap cached multi-store with a version") + panic("cannot branch cached multi-store with a version") } // GetStore returns an underlying Store by key. diff --git a/store/dbadapter/store.go b/store/dbadapter/store.go index fcaeac15ac..e9ea4f847d 100644 --- a/store/dbadapter/store.go +++ b/store/dbadapter/store.go @@ -75,7 +75,7 @@ func (Store) GetStoreType() types.StoreType { return types.StoreTypeDB } -// CacheWrap cache wraps the underlying store. +// CacheWrap branches the underlying store. func (dsa Store) CacheWrap() types.CacheWrap { return cachekv.NewStore(dsa) } diff --git a/store/mem/store.go b/store/mem/store.go index 4657a01140..66591b645f 100644 --- a/store/mem/store.go +++ b/store/mem/store.go @@ -35,7 +35,7 @@ func (s Store) GetStoreType() types.StoreType { return types.StoreTypeMemory } -// CacheWrap cache wraps the underlying store. +// CacheWrap branches the underlying store. func (s Store) CacheWrap() types.CacheWrap { return cachekv.NewStore(s) } diff --git a/store/rootmulti/store.go b/store/rootmulti/store.go index 61378a3661..03fa1b561f 100644 --- a/store/rootmulti/store.go +++ b/store/rootmulti/store.go @@ -41,7 +41,7 @@ const ( ) // Store is composed of many CommitStores. Name contrasts with -// cacheMultiStore which is for cache-wrapping other MultiStores. It implements +// cacheMultiStore which is used for branching other MultiStores. It implements // the CommitMultiStore interface. type Store struct { db dbm.DB @@ -404,7 +404,7 @@ func (rs *Store) CacheWrapWithTrace(_ io.Writer, _ types.TraceContext) types.Cac return rs.CacheWrap() } -// CacheMultiStore cache-wraps the multi-store and returns a CacheMultiStore. +// CacheMultiStore creates ephemeral branch of the multi-store and returns a CacheMultiStore. // It implements the MultiStore interface. func (rs *Store) CacheMultiStore() types.CacheMultiStore { stores := make(map[types.StoreKey]types.CacheWrapper) @@ -841,7 +841,7 @@ func (rs *Store) loadCommitStoreFromParams(key types.StoreKey, id types.CommitID if rs.interBlockCache != nil { // Wrap and get a CommitKVStore with inter-block caching. Note, this should // only wrap the primary CommitKVStore, not any store that is already - // cache-wrapped as that will create unexpected behavior. + // branched as that will create unexpected behavior. store = rs.interBlockCache.GetStoreCache(key, store) } diff --git a/store/tracekv/store.go b/store/tracekv/store.go index 3becdd100b..2958d96826 100644 --- a/store/tracekv/store.go +++ b/store/tracekv/store.go @@ -161,14 +161,14 @@ func (tkv *Store) GetStoreType() types.StoreType { return tkv.parent.GetStoreType() } -// CacheWrap implements the KVStore interface. It panics as a Store -// cannot be cache wrapped. +// CacheWrap implements the KVStore interface. It panics because a Store +// cannot be branched. func (tkv *Store) CacheWrap() types.CacheWrap { panic("cannot CacheWrap a Store") } // CacheWrapWithTrace implements the KVStore interface. It panics as a -// Store cannot be cache wrapped. +// Store cannot be branched. func (tkv *Store) CacheWrapWithTrace(_ io.Writer, _ types.TraceContext) types.CacheWrap { panic("cannot CacheWrapWithTrace a Store") } diff --git a/store/types/store.go b/store/types/store.go index 68b231dfc3..8da2b26fd1 100644 --- a/store/types/store.go +++ b/store/types/store.go @@ -104,12 +104,12 @@ func (s *StoreUpgrades) RenamedFrom(key string) string { type MultiStore interface { Store - // Cache wrap MultiStore. + // Branches MultiStore into a cached storage object. // NOTE: Caller should probably not call .Write() on each, but // call CacheMultiStore.Write(). CacheMultiStore() CacheMultiStore - // CacheMultiStoreWithVersion cache-wraps the underlying MultiStore where + // CacheMultiStoreWithVersion branches the underlying MultiStore where // each stored is loaded at a specific version (height). CacheMultiStoreWithVersion(version int64) (CacheMultiStore, error) @@ -138,7 +138,7 @@ type CacheMultiStore interface { Write() // Writes operations to underlying KVStore } -// A non-cache MultiStore. +// CommitMultiStore is an interface for a MultiStore without cache capabilities. type CommitMultiStore interface { Committer MultiStore @@ -218,12 +218,12 @@ type KVStore interface { ReverseIterator(start, end []byte) Iterator } -// Alias iterator to db's Iterator for convenience. +// Iterator is an alias db's Iterator for convenience. type Iterator = dbm.Iterator -// CacheKVStore cache-wraps a KVStore. After calling .Write() on -// the CacheKVStore, all previously created CacheKVStores on the -// object expire. +// CacheKVStore branches a KVStore and provides read cache functionality. +// After calling .Write() on the CacheKVStore, all previously created +// CacheKVStores on the object expire. type CacheKVStore interface { KVStore @@ -231,7 +231,7 @@ type CacheKVStore interface { Write() } -// Stores of MultiStore must implement CommitStore. +// CommitKVStore is an interface for MultiStore. type CommitKVStore interface { Committer KVStore @@ -240,9 +240,9 @@ type CommitKVStore interface { //---------------------------------------- // CacheWrap -// CacheWrap makes the most appropriate cache-wrap. For example, -// IAVLStore.CacheWrap() returns a CacheKVStore. CacheWrap should not return -// a Committer, since Commit cache-wraps make no sense. It can return KVStore, +// CacheWrap is the most appropriate interface for store ephemeral branching and cache. +// For example, IAVLStore.CacheWrap() returns a CacheKVStore. CacheWrap should not return +// a Committer, since Commit ephemeral store make no sense. It can return KVStore, // HeapStore, SpaceStore, etc. type CacheWrap interface { // Write syncs with the underlying store. @@ -256,10 +256,10 @@ type CacheWrap interface { } type CacheWrapper interface { - // CacheWrap cache wraps. + // CacheWrap branches a store. CacheWrap() CacheWrap - // CacheWrapWithTrace cache wraps with tracing enabled. + // CacheWrapWithTrace branches a store with tracing enabled. CacheWrapWithTrace(w io.Writer, tc TraceContext) CacheWrap } diff --git a/x/distribution/keeper/querier.go b/x/distribution/keeper/querier.go index 3d4c257b00..71370e24ba 100644 --- a/x/distribution/keeper/querier.go +++ b/x/distribution/keeper/querier.go @@ -129,7 +129,7 @@ func queryDelegationRewards(ctx sdk.Context, _ []string, req abci.RequestQuery, return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } - // cache-wrap context as to not persist state changes during querying + // branch the context to isolate state changes ctx, _ = ctx.CacheContext() val := k.stakingKeeper.Validator(ctx, params.ValidatorAddress) @@ -163,7 +163,7 @@ func queryDelegatorTotalRewards(ctx sdk.Context, _ []string, req abci.RequestQue return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } - // cache-wrap context as to not persist state changes during querying + // branch the context to isolate state changes ctx, _ = ctx.CacheContext() total := sdk.DecCoins{} @@ -201,7 +201,7 @@ func queryDelegatorValidators(ctx sdk.Context, _ []string, req abci.RequestQuery return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } - // cache-wrap context as to not persist state changes during querying + // branch the context to isolate state changes ctx, _ = ctx.CacheContext() var validators []sdk.ValAddress @@ -229,7 +229,7 @@ func queryDelegatorWithdrawAddress(ctx sdk.Context, _ []string, req abci.Request return nil, sdkerrors.Wrap(sdkerrors.ErrJSONUnmarshal, err.Error()) } - // cache-wrap context as to not persist state changes during querying + // branch the context to isolate state changes ctx, _ = ctx.CacheContext() withdrawAddr := k.GetDelegatorWithdrawAddr(ctx, params.DelegatorAddress) diff --git a/x/gov/keeper/proposal.go b/x/gov/keeper/proposal.go index c6bc85e35b..14a324d780 100644 --- a/x/gov/keeper/proposal.go +++ b/x/gov/keeper/proposal.go @@ -15,9 +15,9 @@ func (keeper Keeper) SubmitProposal(ctx sdk.Context, content types.Content) (typ return types.Proposal{}, sdkerrors.Wrap(types.ErrNoProposalHandlerExists, content.ProposalRoute()) } - // Execute the proposal content in a cache-wrapped context to validate the - // actual parameter changes before the proposal proceeds through the - // governance process. State is not persisted. + // Execute the proposal content in a new context branch (with branched store) + // to validate the actual parameter changes before the proposal proceeds + // through the governance process. State is not persisted. cacheCtx, _ := ctx.CacheContext() handler := keeper.router.GetRoute(content.ProposalRoute()) if err := handler(cacheCtx, content); err != nil {