Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(server/v2): clean up storage use and config #22008

Merged
merged 31 commits into from
Oct 8, 2024
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
88c746a
refactor(runtime/v2): use StoreBuilder
kocubinski Sep 30, 2024
fe2f122
merge main
kocubinski Sep 30, 2024
5d471b5
remove GetStore() any from AppI interface
kocubinski Oct 1, 2024
058248a
Merge branch 'main' of github.com:cosmos/cosmos-sdk into kocu/store-b…
kocubinski Oct 1, 2024
c8c207c
fix testnet.go
kocubinski Oct 1, 2024
ddab5ee
big store config refactor
kocubinski Oct 2, 2024
06c84bb
go mod tidy all
kocubinski Oct 2, 2024
3d96b52
Merge branch 'main' of github.com:cosmos/cosmos-sdk into kocu/store-b…
kocubinski Oct 2, 2024
9faf4dc
fix go lint
kocubinski Oct 2, 2024
8da9ef2
fix lint
kocubinski Oct 2, 2024
ba09fa0
fix tests with mocks
kocubinski Oct 2, 2024
e1856ab
supply default values
kocubinski Oct 2, 2024
ad5b9ca
revert
kocubinski Oct 3, 2024
70c7598
error handling and omit Home field in TOML
kocubinski Oct 3, 2024
f75ecbf
fix typo
kocubinski Oct 3, 2024
90ed108
Merge branch 'main' of github.com:cosmos/cosmos-sdk into kocu/store-b…
kocubinski Oct 3, 2024
25e8991
add godoc
kocubinski Oct 3, 2024
7decd50
move provider to runtime
kocubinski Oct 3, 2024
84a6198
lint fixes
kocubinski Oct 3, 2024
56c8472
minor nit
kocubinski Oct 3, 2024
eb50987
add docs
kocubinski Oct 3, 2024
ecd1b48
revert go.mod changes
kocubinski Oct 3, 2024
85b2975
delete unused struct
kocubinski Oct 3, 2024
327e74c
sort imports
kocubinski Oct 4, 2024
afc1eca
Merge branch 'main' of github.com:cosmos/cosmos-sdk into kocu/store-b…
kocubinski Oct 4, 2024
5a114e8
Merge branch 'main' into kocu/store-builder
kocubinski Oct 6, 2024
84d319d
Merge branch 'main' of github.com:cosmos/cosmos-sdk into kocu/store-b…
kocubinski Oct 7, 2024
1589f13
Merge branch 'main' of github.com:cosmos/cosmos-sdk into kocu/store-b…
kocubinski Oct 8, 2024
80f819f
fix for tests
kocubinski Oct 8, 2024
d23d13a
fix comment
kocubinski Oct 8, 2024
a857bbd
Merge branch 'main' into kocu/store-builder
kocubinski Oct 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 0 additions & 20 deletions runtime/v2/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ package runtime

import (
"encoding/json"
"errors"
"slices"

runtimev2 "cosmossdk.io/api/cosmos/app/runtime/v2"
appmodulev2 "cosmossdk.io/core/appmodule/v2"
Expand Down Expand Up @@ -36,8 +34,6 @@ type App[T transaction.Tx] struct {
logger log.Logger
config *runtimev2.Module

// modules configuration
storeKeys []string
interfaceRegistrar registry.InterfaceRegistrar
amino registry.AminoRegistrar
moduleManager *MM[T]
Expand Down Expand Up @@ -93,22 +89,6 @@ func (a *App[T]) Close() error {
return nil
}

// GetStoreKeys returns all the app store keys.
func (a *App[T]) GetStoreKeys() []string {
return a.storeKeys
}

// UnsafeFindStoreKey fetches a registered StoreKey from the App in linear time.
// NOTE: This should only be used in testing.
func (a *App[T]) UnsafeFindStoreKey(storeKey string) (string, error) {
i := slices.IndexFunc(a.storeKeys, func(s string) bool { return s == storeKey })
if i == -1 {
return "", errors.New("store key not found")
}

return a.storeKeys[i], nil
}

// GetStore returns the app store.
func (a *App[T]) GetStore() Store {
return a.db
Expand Down
27 changes: 15 additions & 12 deletions runtime/v2/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@ import (
"cosmossdk.io/server/v2/appmanager"
"cosmossdk.io/server/v2/stf"
"cosmossdk.io/server/v2/stf/branch"
"cosmossdk.io/store/v2/root"
)

// AppBuilder is a type that is injected into a container by the runtime/v2 module
// (as *AppBuilder) which can be used to create an app which is compatible with
// the existing app.go initialization conventions.
type AppBuilder[T transaction.Tx] struct {
app *App[T]
app *App[T]
storeBuilder root.Builder

// the following fields are used to overwrite the default
branch func(state store.ReaderMap) store.WriterMap
Expand Down Expand Up @@ -62,14 +64,6 @@ func (a *AppBuilder[T]) RegisterModules(modules map[string]appmodulev2.AppModule
return nil
}

// RegisterStores registers the provided store keys.
// This method should only be used for registering extra stores
// which is necessary for modules that not registered using the app config.
// To be used in combination of RegisterModules.
func (a *AppBuilder[T]) RegisterStores(keys ...string) {
kocubinski marked this conversation as resolved.
Show resolved Hide resolved
a.app.storeKeys = append(a.app.storeKeys, keys...)
}

// Build builds an *App instance.
func (a *AppBuilder[T]) Build(opts ...AppBuilderOption[T]) (*App[T], error) {
for _, opt := range opts {
Expand All @@ -93,8 +87,9 @@ func (a *AppBuilder[T]) Build(opts ...AppBuilderOption[T]) (*App[T], error) {
}
}

a.app.db = a.storeBuilder.Get()
if a.app.db == nil {
return nil, fmt.Errorf("app.db is not set, it is required to build the app")
return nil, fmt.Errorf("storeBuilder did not return a db")
}

if err := a.app.moduleManager.RegisterServices(a.app); err != nil {
Expand Down Expand Up @@ -205,15 +200,23 @@ func AppBuilderWithBranch[T transaction.Tx](branch func(state store.ReaderMap) s

// AppBuilderWithTxValidator sets the tx validator for the app.
// It overrides all default tx validators defined by modules.
func AppBuilderWithTxValidator[T transaction.Tx](txValidators func(ctx context.Context, tx T) error) AppBuilderOption[T] {
func AppBuilderWithTxValidator[T transaction.Tx](
txValidators func(
ctx context.Context, tx T,
) error,
) AppBuilderOption[T] {
return func(a *AppBuilder[T]) {
a.txValidator = txValidators
}
}

// AppBuilderWithPostTxExec sets logic that will be executed after each transaction.
// When not provided, a no-op function will be used.
func AppBuilderWithPostTxExec[T transaction.Tx](postTxExec func(ctx context.Context, tx T, success bool) error) AppBuilderOption[T] {
func AppBuilderWithPostTxExec[T transaction.Tx](
postTxExec func(
ctx context.Context, tx T, success bool,
) error,
) AppBuilderOption[T] {
return func(a *AppBuilder[T]) {
a.postTxExec = postTxExec
}
Expand Down
117 changes: 41 additions & 76 deletions runtime/v2/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,14 @@ import (
"cosmossdk.io/core/event"
"cosmossdk.io/core/header"
"cosmossdk.io/core/registry"
"cosmossdk.io/core/server"
"cosmossdk.io/core/store"
"cosmossdk.io/core/transaction"
"cosmossdk.io/depinject"
"cosmossdk.io/depinject/appconfig"
"cosmossdk.io/log"
"cosmossdk.io/runtime/v2/services"
"cosmossdk.io/server/v2/stf"
rootstore "cosmossdk.io/store/v2/root"
"cosmossdk.io/store/v2/root"
)

var (
Expand Down Expand Up @@ -97,9 +96,9 @@ func init() {
appconfig.Register(&runtimev2.Module{},
appconfig.Provide(
ProvideAppBuilder[transaction.Tx],
ProvideEnvironment[transaction.Tx],
ProvideModuleManager[transaction.Tx],
ProvideStoreBuilder,
ProvideEnvironment,
ProvideKVService,
),
appconfig.Invoke(SetupAppBuilder),
)
Expand All @@ -108,6 +107,7 @@ func init() {
func ProvideAppBuilder[T transaction.Tx](
interfaceRegistrar registry.InterfaceRegistrar,
amino registry.AminoRegistrar,
storeBuilder root.Builder,
) (
*AppBuilder[T],
*stf.MsgRouterBuilder,
Expand All @@ -127,15 +127,14 @@ func ProvideAppBuilder[T transaction.Tx](

msgRouterBuilder := stf.NewMsgRouterBuilder()
app := &App[T]{
storeKeys: nil,
interfaceRegistrar: interfaceRegistrar,
amino: amino,
msgRouterBuilder: msgRouterBuilder,
queryRouterBuilder: stf.NewMsgRouterBuilder(), // TODO dedicated query router
QueryHandlers: map[string]appmodulev2.Handler{},
storeLoader: DefaultStoreLoader,
}
appBuilder := &AppBuilder[T]{app: app}
appBuilder := &AppBuilder[T]{app: app, storeBuilder: storeBuilder}

return appBuilder, msgRouterBuilder, appModule[T]{app}, protoFiles, protoTypes
}
Expand All @@ -149,12 +148,7 @@ type AppInputs struct {
InterfaceRegistrar registry.InterfaceRegistrar
LegacyAmino registry.AminoRegistrar
Logger log.Logger
// StoreBuilder is a builder for a store/v2 RootStore satisfying the Store interface
StoreBuilder *StoreBuilder
// StoreOptions are required as input for the StoreBuilder. If not provided, the default options are used.
StoreOptions *rootstore.Options `optional:"true"`
// DynamicConfig can be nil in client wiring, but is required in server wiring.
DynamicConfig server.DynamicConfig `optional:"true"`
StoreBuilder root.Builder
}

func SetupAppBuilder(inputs AppInputs) {
Expand All @@ -164,24 +158,8 @@ func SetupAppBuilder(inputs AppInputs) {
app.moduleManager = inputs.ModuleManager
app.moduleManager.RegisterInterfaces(inputs.InterfaceRegistrar)
app.moduleManager.RegisterLegacyAminoCodec(inputs.LegacyAmino)

if inputs.DynamicConfig == nil {
return
}
storeOptions := rootstore.DefaultStoreOptions()
if inputs.StoreOptions != nil {
storeOptions = *inputs.StoreOptions
}
var err error
app.db, err = inputs.StoreBuilder.Build(
inputs.Logger,
app.storeKeys,
inputs.DynamicConfig,
storeOptions,
)
if err != nil {
panic(err)
}
// STF requires some state to run
inputs.StoreBuilder.RegisterKey("stf")
}

func ProvideModuleManager[T transaction.Tx](
Expand All @@ -192,44 +170,47 @@ func ProvideModuleManager[T transaction.Tx](
return NewModuleManager[T](logger, config, modules)
}

// ProvideEnvironment provides the environment for keeper modules, while maintaining backward compatibility and provide services directly as well.
func ProvideEnvironment[T transaction.Tx](
logger log.Logger,
func ProvideKVService(
config *runtimev2.Module,
key depinject.ModuleKey,
appBuilder *AppBuilder[T],
kvFactory store.KVStoreServiceFactory,
headerService header.Service,
eventService event.Service,
) (
appmodulev2.Environment,
store.KVStoreService,
store.MemoryStoreService,
) {
var (
kvService store.KVStoreService = failingStoreService{}
memKvService store.MemoryStoreService = failingStoreService{}
)

storeBuilder root.Builder,
) (store.KVStoreService, store.MemoryStoreService) {
// skips modules that have no store
if !slices.Contains(config.SkipStoreKeys, key.Name()) {
var kvStoreKey string
storeKeyOverride := storeKeyOverride(config, key.Name())
if storeKeyOverride != nil {
kvStoreKey = storeKeyOverride.KvStoreKey
} else {
kvStoreKey = key.Name()
}
if slices.Contains(config.SkipStoreKeys, key.Name()) {
return &failingStoreService{}, &failingStoreService{}
}
var kvStoreKey string
override := storeKeyOverride(config, key.Name())
if override != nil {
kvStoreKey = override.KvStoreKey
} else {
kvStoreKey = key.Name()
}

registerStoreKey(appBuilder, kvStoreKey)
kvService = kvFactory([]byte(kvStoreKey))
storeBuilder.RegisterKey(kvStoreKey)
return kvFactory([]byte(kvStoreKey)), stf.NewMemoryStoreService([]byte(fmt.Sprintf("memory:%s", kvStoreKey)))
}

memStoreKey := fmt.Sprintf("memory:%s", key.Name())
registerStoreKey(appBuilder, memStoreKey)
memKvService = stf.NewMemoryStoreService([]byte(memStoreKey))
func storeKeyOverride(config *runtimev2.Module, moduleName string) *runtimev2.StoreKeyConfig {
for _, cfg := range config.OverrideStoreKeys {
if cfg.ModuleName == moduleName {
return cfg
}
}
return nil
}

env := appmodulev2.Environment{
// ProvideEnvironment provides the environment for keeper modules, while maintaining backward compatibility and provide services directly as well.
func ProvideEnvironment(
logger log.Logger,
key depinject.ModuleKey,
kvService store.KVStoreService,
memKvService store.MemoryStoreService,
headerService header.Service,
eventService event.Service,
) appmodulev2.Environment {
return appmodulev2.Environment{
Logger: logger,
BranchService: stf.BranchService{},
EventService: eventService,
Expand All @@ -241,22 +222,6 @@ func ProvideEnvironment[T transaction.Tx](
KVStoreService: kvService,
MemStoreService: memKvService,
}

return env, kvService, memKvService
}

func registerStoreKey[T transaction.Tx](builder *AppBuilder[T], key string) {
builder.app.storeKeys = append(builder.app.storeKeys, key)
}

func storeKeyOverride(config *runtimev2.Module, moduleName string) *runtimev2.StoreKeyConfig {
julienrbrt marked this conversation as resolved.
Show resolved Hide resolved
for _, cfg := range config.OverrideStoreKeys {
if cfg.ModuleName == moduleName {
return cfg
}
}

return nil
}

// DefaultServiceBindings provides default services for the following service interfaces:
Expand Down
71 changes: 15 additions & 56 deletions runtime/v2/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,29 @@ package runtime
import (
"errors"
"fmt"
"path/filepath"
"sync"

"cosmossdk.io/core/server"
"cosmossdk.io/core/store"
"cosmossdk.io/log"
"cosmossdk.io/server/v2/stf"
storev2 "cosmossdk.io/store/v2"
"cosmossdk.io/store/v2/db"
"cosmossdk.io/store/v2/proof"
"cosmossdk.io/store/v2/root"
)

var (
storeBuilderSingleton root.Builder
storeBuilderSingletoneOnce sync.Once
)

// ProvideSingletonScopedStoreBuilder returns a store builder that is a singleton
// in the scope of the process lifetime.
func ProvideSingletonScopedStoreBuilder() root.Builder {
storeBuilderSingletoneOnce.Do(func() {
storeBuilderSingleton = root.NewBuilder()
})
return storeBuilderSingleton
}

// NewKVStoreService creates a new KVStoreService.
// This wrapper is kept for backwards compatibility.
// When migrating from runtime to runtime/v2, use runtimev2.NewKVStoreService(storeKey.Name()) instead of runtime.NewKVStoreService(storeKey).
Expand Down Expand Up @@ -64,58 +75,6 @@ type Store interface {
LastCommitID() (proof.CommitID, error)
}

// StoreBuilder is a builder for a store/v2 RootStore satisfying the Store interface.
type StoreBuilder struct {
store Store
}

// Build creates a new store/v2 RootStore.
func (sb *StoreBuilder) Build(
logger log.Logger,
storeKeys []string,
config server.DynamicConfig,
options root.Options,
) (Store, error) {
if sb.store != nil {
return sb.store, nil
}
home := config.GetString(flagHome)
scRawDb, err := db.NewDB(
db.DBType(config.GetString("store.app-db-backend")),
"application",
filepath.Join(home, "data"),
nil,
)
if err != nil {
return nil, fmt.Errorf("failed to create SCRawDB: %w", err)
}

factoryOptions := &root.FactoryOptions{
Logger: logger,
RootDir: home,
Options: options,
// STF needs to store a bit of state
StoreKeys: append(storeKeys, "stf"),
SCRawDB: scRawDb,
}

rs, err := root.CreateRootStore(factoryOptions)
if err != nil {
return nil, fmt.Errorf("failed to create root store: %w", err)
}
sb.store = rs
return sb.store, nil
}

// Get returns the Store. Build must be called before calling Get or the result will be nil.
func (sb *StoreBuilder) Get() Store {
return sb.store
}

func ProvideStoreBuilder() *StoreBuilder {
return &StoreBuilder{}
}

// StoreLoader allows for custom loading of the store, this is useful when upgrading the store from a previous version
type StoreLoader func(store Store) error

Expand Down
Loading
Loading