diff --git a/baseapp/abci.go b/baseapp/abci.go index 596cd099d..9ef86eba0 100644 --- a/baseapp/abci.go +++ b/baseapp/abci.go @@ -13,20 +13,20 @@ import ( "time" "github.com/armon/go-metrics" - "github.com/gogo/protobuf/proto" - abci "github.com/tendermint/tendermint/abci/types" - tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - "google.golang.org/grpc/codes" - grpcstatus "google.golang.org/grpc/status" - "github.com/cosmos/cosmos-sdk/codec" snapshottypes "github.com/cosmos/cosmos-sdk/snapshots/types" + "github.com/cosmos/cosmos-sdk/store/types" "github.com/cosmos/cosmos-sdk/tasks" "github.com/cosmos/cosmos-sdk/telemetry" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/types/legacytm" "github.com/cosmos/cosmos-sdk/utils" + "github.com/gogo/protobuf/proto" + abci "github.com/tendermint/tendermint/abci/types" + tmproto "github.com/tendermint/tendermint/proto/tendermint/types" + "google.golang.org/grpc/codes" + grpcstatus "google.golang.org/grpc/status" ) // InitChain implements the ABCI interface. It runs the initialization logic @@ -708,7 +708,8 @@ func checkNegativeHeight(height int64) error { // CreateQueryContext creates a new sdk.Context for a query, taking as args // the block height and whether the query needs a proof or not. func (app *BaseApp) CreateQueryContext(height int64, prove bool) (sdk.Context, error) { - if err := checkNegativeHeight(height); err != nil { + err := checkNegativeHeight(height) + if err != nil { return sdk.Context{}, err } @@ -734,7 +735,13 @@ func (app *BaseApp) CreateQueryContext(height int64, prove bool) (sdk.Context, e ) } - cacheMS, err := app.cms.CacheMultiStoreWithVersion(height) + var cacheMS types.CacheMultiStore + if height < app.migrationHeight && app.qms != nil { + cacheMS, err = app.qms.CacheMultiStoreWithVersion(height) + } else { + cacheMS, err = app.cms.CacheMultiStoreWithVersion(height) + } + if err != nil { return sdk.Context{}, sdkerrors.Wrapf( @@ -905,12 +912,25 @@ func handleQueryApp(app *BaseApp, path []string, req abci.RequestQuery) abci.Res } func handleQueryStore(app *BaseApp, path []string, req abci.RequestQuery) abci.ResponseQuery { - // "/store" prefix for store queries - queryable, ok := app.cms.(sdk.Queryable) - if !ok { - return sdkerrors.QueryResultWithDebug(sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "multistore doesn't support queries"), app.trace) + + var ( + queryable sdk.Queryable + ok bool + ) + // Check if online migration is enabled for fallback read + if req.Height < app.migrationHeight && app.qms != nil { + queryable, ok = app.qms.(sdk.Queryable) + if !ok { + return sdkerrors.QueryResultWithDebug(sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "multistore doesn't support queries"), app.trace) + } + } else { + queryable, ok = app.cms.(sdk.Queryable) + if !ok { + return sdkerrors.QueryResultWithDebug(sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "multistore doesn't support queries"), app.trace) + } } + // "/store" prefix for store queries req.Path = "/" + strings.Join(path[1:], "/") if req.Height <= 1 && req.Prove { diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index 5e3f1cf7f..cba0551fd 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -179,9 +179,11 @@ type BaseApp struct { //nolint: maligned } type appStore struct { - db dbm.DB // common DB backend - cms sdk.CommitMultiStore // Main (uncached) state - storeLoader StoreLoader // function to handle store loading, may be overridden with SetStoreLoader() + db dbm.DB // common DB backend + cms sdk.CommitMultiStore // Main (uncached) state + qms sdk.MultiStore // Query multistore used for migration only + migrationHeight int64 + storeLoader StoreLoader // function to handle store loading, may be overridden with SetStoreLoader() // an inter-block write-through cache provided to the context during deliverState interBlockCache sdk.MultiStorePersistentCache diff --git a/baseapp/options.go b/baseapp/options.go index 316cbbee0..3d65ea8a8 100644 --- a/baseapp/options.go +++ b/baseapp/options.go @@ -361,3 +361,13 @@ func (app *BaseApp) SetStreamingService(s StreamingService) { // BaseApp will pass BeginBlock, DeliverTx, and EndBlock requests and responses to the streaming services to update their ABCI context app.abciListeners = append(app.abciListeners, s) } + +// SetQueryMultiStore set a alternative MultiStore implementation to support online migration fallback read. +func (app *BaseApp) SetQueryMultiStore(ms sdk.MultiStore) { + app.qms = ms +} + +// SetMigrationHeight set the migration height for online migration so that query below this height will still be served from IAVL. +func (app *BaseApp) SetMigrationHeight(height int64) { + app.migrationHeight = height +}