Skip to content

Commit

Permalink
feat: add ParamsQuerier
Browse files Browse the repository at this point in the history
  • Loading branch information
bryanchriswhite committed Dec 11, 2024
1 parent c9cd228 commit 48f5762
Show file tree
Hide file tree
Showing 3 changed files with 189 additions and 0 deletions.
1 change: 1 addition & 0 deletions pkg/client/interface.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
//go:generate mockgen -destination=../../testutil/mockclient/grpc_conn_mock.go -package=mockclient github.com/cosmos/gogoproto/grpc ClientConn
//go:generate mockgen -destination=../../testutil/mockclient/events_query_client_mock.go -package=mockclient . Dialer,Connection,EventsQueryClient
//go:generate mockgen -destination=../../testutil/mockclient/block_client_mock.go -package=mockclient . Block,BlockClient
//go:generate mockgen -destination=../../testutil/mockclient/delegation_client_mock.go -package=mockclient . DelegationClient
Expand Down
57 changes: 57 additions & 0 deletions pkg/client/query/options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package query

import (
sdkerrors "cosmossdk.io/errors"
cosmostypes "github.com/cosmos/cosmos-sdk/types"

"github.com/pokt-network/poktroll/pkg/client/query/cache"
)

// ParamsQuerierConfig holds the configuration for parameter queriers
type ParamsQuerierConfig struct {
// CacheOpts are the options passed to create the params cache
CacheOpts []cache.CacheOption
// ModuleName is used for logging and error context
ModuleName string
// ModuleParamError is the base error type for parameter query errors
ModuleParamError *sdkerrors.Error
}

// ParamsQuerierOptionFn defines a function that configures a ParamsQuerierConfig
type ParamsQuerierOptionFn func(*ParamsQuerierConfig)

// DefaultParamsQuerierConfig returns the default configuration for parameter queriers
func DefaultParamsQuerierConfig() *ParamsQuerierConfig {
return &ParamsQuerierConfig{
CacheOpts: []cache.CacheOption{
// TODO_IN_THIS_COMMIT: extract to constants.
cache.WithHistoricalMode(100),
// TODO_IN_THIS_COMMIT: reconcile the fact that MaxKeys doesn't apply to historical mode...
cache.WithMaxKeys(1),
// TODO_IN_THIS_COMMIT: extract to constants.
cache.WithEvictionPolicy(cache.FirstInFirstOut),
},
}
}

// WithModuleInfo sets the module-specific information for the querier
func WithModuleInfo[R cosmostypes.Msg](moduleName string, moduleParamError *sdkerrors.Error) ParamsQuerierOptionFn {
return func(cfg *ParamsQuerierConfig) {
cfg.ModuleName = moduleName
cfg.ModuleParamError = moduleParamError
}
}

// WithParamsCacheOptions adds cache configuration options to the params querier
func WithParamsCacheOptions(opts ...cache.CacheOption) ParamsQuerierOptionFn {
return func(cfg *ParamsQuerierConfig) {
cfg.CacheOpts = append(cfg.CacheOpts, opts...)
}
}

// WithCacheOptions adds cache configuration options to the shared querier
func WithCacheOptions(opts ...cache.CacheOption) ParamsQuerierOptionFn {
return func(cfg *ParamsQuerierConfig) {
cfg.CacheOpts = append(cfg.CacheOpts, opts...)
}
}
131 changes: 131 additions & 0 deletions pkg/client/query/paramsquerier.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package query

import (
"context"
"errors"

"cosmossdk.io/depinject"
cosmostypes "github.com/cosmos/cosmos-sdk/types"
gogogrpc "github.com/cosmos/gogoproto/grpc"

"github.com/pokt-network/poktroll/pkg/client"
"github.com/pokt-network/poktroll/pkg/client/query/cache"
"github.com/pokt-network/poktroll/pkg/polylog"
)

var _ client.ParamsQuerier[cosmostypes.Msg] = (*cachedParamsQuerier[cosmostypes.Msg, paramsQuerierIface[cosmostypes.Msg]])(nil)

// paramsQuerierIface is an interface which generated query clients MUST implement
// to be compatible with the cachedParamsQuerier.
// DEV_NOTE: It is mainly required due to syntactic constraints imposed by the generics
// (i.e. otherwise, P here MUST be a value type, and there's no way to express that Q
// (below) SHOULD be the concrete type of P in NewCachedParamsQuerier).
type paramsQuerierIface[P cosmostypes.Msg] interface {
GetParams(context.Context) (P, error)
}

// NewCachedParamsQuerier creates a new params querier with the given configuration
func NewCachedParamsQuerier[P cosmostypes.Msg, Q paramsQuerierIface[P]](
deps depinject.Config,
queryClientConstructor func(conn gogogrpc.ClientConn) Q,
opts ...ParamsQuerierOptionFn,
) (_ client.ParamsQuerier[P], err error) {
cfg := DefaultParamsQuerierConfig()
for _, opt := range opts {
opt(cfg)
}

querier := &cachedParamsQuerier[P, Q]{
config: cfg,
paramsCache: cache.NewInMemoryCache[P](cfg.CacheOpts...),
}

if err = depinject.Inject(
deps,
&querier.clientConn,
); err != nil {
return nil, err
}

querier.queryClient = queryClientConstructor(querier.clientConn)

return querier, nil
}

// TODO_IN_THIS_COMMIT: update godoc...
// cachedParamsQuerier provides common functionality for all params queriers.
// It handles parameter caching and chain querying in a generic way, where
// R is the type of the parameters and Q is the type of the query client.
type cachedParamsQuerier[P cosmostypes.Msg, Q paramsQuerierIface[P]] struct {
clientConn gogogrpc.ClientConn
queryClient Q
paramsCache client.HistoricalQueryCache[P]
config *ParamsQuerierConfig
}

// TODO_IN_THIS_COMMIT: update godoc...
// GetParams implements the common parameter querying with caching
func (bq *cachedParamsQuerier[P, Q]) GetParams(ctx context.Context) (P, error) {
logger := polylog.Ctx(ctx).With(
"querier", bq.config.ModuleName,
"method", "GetParams",
)

// Check cache first
var paramsZero P
cached, err := bq.paramsCache.Get("params")
switch {
case err == nil:
logger.Debug().Msg("cache hit")
return cached, nil
case !errors.Is(err, cache.ErrCacheMiss):
return paramsZero, err
}

logger.Debug().Msg("cache miss")

// Query chain on cache miss
params, err := bq.queryClient.GetParams(ctx)
if err != nil {
if bq.config.ModuleParamError != nil {
return paramsZero, bq.config.ModuleParamError.Wrap(err.Error())
}
return paramsZero, err
}

// Cache the result before returning
if err = bq.paramsCache.Set("params", params); err != nil {
return paramsZero, err
}

return params, nil
}

// TODO_IN_THIS_COMMIT: update godoc...
// GetParamsAtHeight returns parameters as they were at a specific height
func (bq *cachedParamsQuerier[P, Q]) GetParamsAtHeight(ctx context.Context, height int64) (P, error) {
logger := polylog.Ctx(ctx).With(
"querier", bq.config.ModuleName,
"method", "GetParamsAtHeight",
"height", height,
)

// Try to get from cache at specific height
cached, err := bq.paramsCache.GetAtHeight("params", height)
switch {
case err == nil:
logger.Debug().Msg("cache hit")
return cached, nil
case !errors.Is(err, cache.ErrCacheMiss):
return cached, err
}

logger.Debug().Msg("cache miss")

// TODO_MAINNET(@bryanchriswhite): Implement querying historical params from chain
err = cache.ErrCacheMiss.Wrapf("TODO: on-chain historical data not implemented")
logger.Error().Msgf("%s", err)

// Meanwhile, return current params as fallback. 😬
return bq.GetParams(ctx)
}

0 comments on commit 48f5762

Please sign in to comment.