Skip to content

Commit b4b4951

Browse files
mergify[bot]testinginprodczarcas7ic
authored
fix(StargateQueries): use a sync pool when unmarshalling responses of protobuf objects (backport #7346) (#7349)
* fix(StargateQueries): use a sync pool when unmarshalling responses of protobuf objects (#7346) * use a sync pool when unmarshalling responses of protobuf objects in StargateQueries * fix uninitted pool * type assertion and lints * changelog * add comment for returnStargateResponseToPool * add setWhitelistedQuery comment * lint --------- Co-authored-by: unknown unknown <unknown@unknown> Co-authored-by: Adam Tucker <[email protected]> (cherry picked from commit 2caa5c6) # Conflicts: # CHANGELOG.md * changelog --------- Co-authored-by: testinginprod <[email protected]> Co-authored-by: Adam Tucker <[email protected]>
1 parent e98fdfd commit b4b4951

File tree

5 files changed

+62
-24
lines changed

5 files changed

+62
-24
lines changed

CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@ All notable changes to this project will be documented in this file.
4040
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
4141
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
4242

43+
## v22.0.1
44+
45+
### Bug Fixes
46+
47+
* [#7346](https://github.com/osmosis-labs/osmosis/pull/7346) Prevent heavy gRPC load from app hashing nodes
48+
4349
## v22.0.0
4450

4551
### Fee Market Parameter Updates

cmd/osmosisd/cmd/stargate-query.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ Example:
7878
//nolint:staticcheck
7979
func GetStructAndFill(queryPath, module, structName string, structArguments ...string) (interface{}, error) {
8080
const ParamRequest = "QueryParamsRequest"
81-
_, err := wasmbinding.GetWhitelistedQuery(queryPath)
81+
err := wasmbinding.IsWhitelistedQuery(queryPath)
8282
if err != nil {
8383
return nil, err
8484
}

wasmbinding/export_test.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ package wasmbinding
22

33
import "github.com/cosmos/cosmos-sdk/codec"
44

5-
func SetWhitelistedQuery(queryPath string, protoType codec.ProtoMarshaler) {
5+
func SetWhitelistedQuery[T any, PT protoTypeG[T]](queryPath string, protoType PT) {
66
setWhitelistedQuery(queryPath, protoType)
77
}
8+
9+
func GetWhitelistedQuery(queryPath string) (codec.ProtoMarshaler, error) {
10+
return getWhitelistedQuery(queryPath)
11+
}

wasmbinding/query_plugin.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,15 @@ import (
1717
// StargateQuerier dispatches whitelisted stargate queries
1818
func StargateQuerier(queryRouter baseapp.GRPCQueryRouter, cdc codec.Codec) func(ctx sdk.Context, request *wasmvmtypes.StargateQuery) ([]byte, error) {
1919
return func(ctx sdk.Context, request *wasmvmtypes.StargateQuery) ([]byte, error) {
20-
protoResponseType, err := GetWhitelistedQuery(request.Path)
20+
protoResponseType, err := getWhitelistedQuery(request.Path)
2121
if err != nil {
2222
return nil, err
2323
}
2424

25+
// no matter what happens after this point, we must return
26+
// the response type to prevent sync.Pool from leaking.
27+
defer returnStargateResponseToPool(request.Path, protoResponseType)
28+
2529
route := queryRouter.Route(request.Path)
2630
if route == nil {
2731
return nil, wasmvmtypes.UnsupportedRequest{Kind: fmt.Sprintf("No route to query '%s'", request.Path)}

wasmbinding/stargate_whitelist.go

+45-21
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,15 @@ import (
3333
epochtypes "github.com/osmosis-labs/osmosis/x/epochs/types"
3434
)
3535

36-
// stargateWhitelist keeps whitelist and its deterministic
36+
// stargateResponsePools keeps whitelist and its deterministic
3737
// response binding for stargate queries.
3838
// CONTRACT: since results of queries go into blocks, queries being added here should always be
3939
// deterministic or can cause non-determinism in the state machine.
4040
//
41-
// The query can be multi-thread, so we have to use
42-
// thread safe sync.Map.
43-
var stargateWhitelist sync.Map
41+
// The query is multi-threaded so we're using a sync.Pool
42+
// to manage the allocation and de-allocation of newly created
43+
// pb objects.
44+
var stargateResponsePools = make(map[string]*sync.Pool)
4445

4546
// Note: When adding a migration here, we should also add it to the Async ICQ params in the upgrade.
4647
// In the future we may want to find a better way to keep these in sync
@@ -184,34 +185,57 @@ func init() {
184185
setWhitelistedQuery("/osmosis.concentratedliquidity.v1beta1.Query/CFMMPoolIdLinkFromConcentratedPoolId", &concentratedliquidityquery.CFMMPoolIdLinkFromConcentratedPoolIdResponse{})
185186
}
186187

187-
// GetWhitelistedQuery returns the whitelisted query at the provided path.
188+
// IsWhitelistedQuery returns if the query is not whitelisted.
189+
func IsWhitelistedQuery(queryPath string) error {
190+
_, isWhitelisted := stargateResponsePools[queryPath]
191+
if !isWhitelisted {
192+
return wasmvmtypes.UnsupportedRequest{Kind: fmt.Sprintf("'%s' path is not allowed from the contract", queryPath)}
193+
}
194+
return nil
195+
}
196+
197+
// getWhitelistedQuery returns the whitelisted query at the provided path.
188198
// If the query does not exist, or it was setup wrong by the chain, this returns an error.
189-
func GetWhitelistedQuery(queryPath string) (codec.ProtoMarshaler, error) {
190-
protoResponseAny, isWhitelisted := stargateWhitelist.Load(queryPath)
199+
// CONTRACT: must call returnStargateResponseToPool in order to avoid pointless allocs.
200+
func getWhitelistedQuery(queryPath string) (codec.ProtoMarshaler, error) {
201+
protoResponseAny, isWhitelisted := stargateResponsePools[queryPath]
191202
if !isWhitelisted {
192203
return nil, wasmvmtypes.UnsupportedRequest{Kind: fmt.Sprintf("'%s' path is not allowed from the contract", queryPath)}
193204
}
194-
protoResponseType, ok := protoResponseAny.(codec.ProtoMarshaler)
205+
protoMarshaler, ok := protoResponseAny.Get().(codec.ProtoMarshaler)
195206
if !ok {
196-
return nil, wasmvmtypes.Unknown{}
207+
return nil, fmt.Errorf("failed to assert type to codec.ProtoMarshaler")
197208
}
198-
return protoResponseType, nil
209+
return protoMarshaler, nil
199210
}
200211

201-
func setWhitelistedQuery(queryPath string, protoType codec.ProtoMarshaler) {
202-
stargateWhitelist.Store(queryPath, protoType)
212+
type protoTypeG[T any] interface {
213+
*T
214+
codec.ProtoMarshaler
215+
}
216+
217+
// setWhitelistedQuery sets the whitelisted query at the provided path.
218+
// This method also creates a sync.Pool for the provided protoMarshaler.
219+
// We use generics so we can properly instantiate an object that the
220+
// queryPath expects as a response.
221+
func setWhitelistedQuery[T any, PT protoTypeG[T]](queryPath string, _ PT) {
222+
stargateResponsePools[queryPath] = &sync.Pool{
223+
New: func() any {
224+
return PT(new(T))
225+
},
226+
}
227+
}
228+
229+
// returnStargateResponseToPool returns the provided protoMarshaler to the appropriate pool based on it's query path.
230+
func returnStargateResponseToPool(queryPath string, pb codec.ProtoMarshaler) {
231+
stargateResponsePools[queryPath].Put(pb)
203232
}
204233

205234
func GetStargateWhitelistedPaths() (keys []string) {
206235
// Iterate over the map and collect the keys
207-
stargateWhitelist.Range(func(key, value interface{}) bool {
208-
keyStr, ok := key.(string)
209-
if !ok {
210-
panic("key is not a string")
211-
}
212-
keys = append(keys, keyStr)
213-
return true
214-
})
215-
236+
keys = make([]string, 0, len(stargateResponsePools))
237+
for k := range stargateResponsePools {
238+
keys = append(keys, k)
239+
}
216240
return keys
217241
}

0 commit comments

Comments
 (0)