Skip to content

Commit cad51f5

Browse files
authored
Merge pull request #73 from wemixarchive/api-performance
merge v0.10.7 to master
2 parents 9f61c5f + 35167c8 commit cad51f5

9 files changed

Lines changed: 314 additions & 10 deletions

File tree

cmd/geth/main.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,9 @@ var (
225225
utils.BlockMinBuildTime,
226226
utils.BlockMinBuildTxs,
227227
utils.BlockTrailTime,
228+
utils.PublicRequestsCacheLocation,
229+
utils.MaxPublicRequests,
230+
utils.BootnodeCount,
228231
}
229232
)
230233

cmd/geth/usage.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,9 @@ var AppHelpFlagGroups = []flags.FlagGroup{
235235
utils.BlockMinBuildTime,
236236
utils.BlockMinBuildTxs,
237237
utils.BlockTrailTime,
238+
utils.PublicRequestsCacheLocation,
239+
utils.MaxPublicRequests,
240+
utils.BootnodeCount,
238241
},
239242
},
240243
{

cmd/utils/flags.go

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"io"
2424
"math"
2525
"math/big"
26+
"math/rand"
2627
"os"
2728
"path/filepath"
2829
godebug "runtime/debug"
@@ -916,6 +917,21 @@ var (
916917
Usage: "Time to leave for block data transfer in ms",
917918
Value: params.BlockTrailTime,
918919
}
920+
PublicRequestsCacheLocation = cli.StringFlag{
921+
Name: "wemix.publicrequests.cache",
922+
Usage: "Public requests cache location",
923+
Value: params.PublicRequestsCacheLocation,
924+
}
925+
MaxPublicRequests = cli.Int64Flag{
926+
Name: "wemix.publicrequests.max",
927+
Usage: "Max # of concurrent public requests",
928+
Value: params.MaxPublicRequests,
929+
}
930+
BootnodeCount = cli.IntFlag{
931+
Name: "wemix.bootnodecount",
932+
Usage: "Default bootnode peer count",
933+
Value: params.BootnodeCount,
934+
}
919935
)
920936

921937
var (
@@ -1011,15 +1027,40 @@ func setNodeUserIdent(ctx *cli.Context, cfg *node.Config) {
10111027
}
10121028
}
10131029

1030+
// setRandomBootstrapNodes setting a random list of bootstrap nodes using the command line
1031+
func setRandomBootstrapNodes(ctx *cli.Context, bootnodes []string) []string {
1032+
rand.Seed(time.Now().UnixNano())
1033+
bootnodeslen := len(bootnodes)
1034+
1035+
// check command line
1036+
if ctx.GlobalIsSet(BootnodeCount.Name) {
1037+
setcount := ctx.GlobalInt(BootnodeCount.Name)
1038+
if setcount > 0 && setcount <= bootnodeslen {
1039+
params.BootnodeCount = setcount
1040+
}
1041+
}
1042+
// select random bootnodes
1043+
selectcount := params.BootnodeCount
1044+
urls := make([]string, selectcount)
1045+
tempnode := make([]string, bootnodeslen)
1046+
copy(tempnode, bootnodes)
1047+
for i := 0; i < selectcount; i++ {
1048+
index := rand.Intn(len(tempnode))
1049+
urls = append(urls, tempnode[index])
1050+
tempnode = append(tempnode[:index], tempnode[index+1:]...)
1051+
}
1052+
return urls
1053+
}
1054+
10141055
// setBootstrapNodes creates a list of bootstrap nodes from the command line
10151056
// flags, reverting to pre-configured ones if none have been specified.
10161057
func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) {
1017-
urls := params.WemixMainnetBootnodes
1058+
urls := setRandomBootstrapNodes(ctx, params.WemixMainnetBootnodes)
10181059
switch {
10191060
case ctx.GlobalIsSet(BootnodesFlag.Name):
10201061
urls = SplitAndTrim(ctx.GlobalString(BootnodesFlag.Name))
10211062
case ctx.GlobalBool(WemixTestnetFlag.Name):
1022-
urls = params.WemixTestnetBootnodes
1063+
urls = setRandomBootstrapNodes(ctx, params.WemixTestnetBootnodes)
10231064
case ctx.GlobalBool(RopstenFlag.Name):
10241065
urls = params.RopstenBootnodes
10251066
case ctx.GlobalBool(SepoliaFlag.Name):
@@ -1982,6 +2023,12 @@ func SetWemixConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
19822023
if ctx.GlobalIsSet(BlockTrailTime.Name) {
19832024
params.BlockTrailTime = ctx.GlobalInt64(BlockTrailTime.Name)
19842025
}
2026+
if ctx.GlobalIsSet(PublicRequestsCacheLocation.Name) {
2027+
params.PublicRequestsCacheLocation = ctx.GlobalString(PublicRequestsCacheLocation.Name)
2028+
}
2029+
if ctx.GlobalIsSet(MaxPublicRequests.Name) {
2030+
params.MaxPublicRequests = ctx.GlobalInt64(MaxPublicRequests.Name)
2031+
}
19852032

19862033
if params.ConsensusMethod == params.ConsensusInvalid {
19872034
params.ConsensusMethod = params.ConsensusPoW

eth/api.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,7 @@ func (api *PrivateDebugAPI) GetBadBlocks(ctx context.Context) ([]*BadBlockArgs,
412412
} else {
413413
blockRlp = fmt.Sprintf("0x%x", rlpBytes)
414414
}
415-
if blockJSON, err = ethapi.RPCMarshalBlock(block, true, true, api.eth.APIBackend.ChainConfig()); err != nil {
415+
if blockJSON, err = ethapi.RPCMarshalBlock(ctx, block, true, true, api.eth.APIBackend.ChainConfig()); err != nil {
416416
blockJSON = map[string]interface{}{"error": err.Error()}
417417
}
418418
results = append(results, &BadBlockArgs{

eth/protocols/eth/peer.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import (
3434
const (
3535
// maxKnownTxs is the maximum transactions hashes to keep in the known list
3636
// before starting to randomly evict them.
37-
maxKnownTxs = 2000000
37+
maxKnownTxs = 100000
3838

3939
// maxKnownBlocks is the maximum block hashes to keep in the known list
4040
// before starting to randomly evict them.

internal/ethapi/api.go

Lines changed: 126 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,12 @@ import (
2020
"context"
2121
"errors"
2222
"fmt"
23+
"io"
2324
"math/big"
2425
"os"
26+
"runtime"
2527
"strings"
28+
"sync"
2629
"time"
2730

2831
"github.com/davecgh/go-spew/spew"
@@ -42,6 +45,7 @@ import (
4245
"github.com/ethereum/go-ethereum/crypto"
4346
"github.com/ethereum/go-ethereum/crypto/vrf"
4447
"github.com/ethereum/go-ethereum/eth/tracers/logger"
48+
"github.com/ethereum/go-ethereum/ethdb"
4549
"github.com/ethereum/go-ethereum/log"
4650
"github.com/ethereum/go-ethereum/p2p"
4751
"github.com/ethereum/go-ethereum/params"
@@ -50,6 +54,29 @@ import (
5054
"github.com/tyler-smith/go-bip39"
5155
)
5256

57+
var apiRequestsCache ethdb.Database
58+
var apiRequestsThrottle chan struct{}
59+
var apiRequestsTokens chan struct{}
60+
61+
func apiRequestsEnter() {
62+
if len(apiRequestsThrottle) >= int(params.MaxPublicRequests) {
63+
pc, _, _, _ := runtime.Caller(1)
64+
var name string
65+
parts := strings.Split(runtime.FuncForPC(pc).Name(), ".")
66+
if len(parts) > 0 {
67+
name = parts[len(parts)-1]
68+
} else {
69+
name = runtime.FuncForPC(pc).Name()
70+
}
71+
log.Warn("Too many API requests", "func", name, "count", len(apiRequestsThrottle))
72+
}
73+
apiRequestsThrottle <- struct{}{}
74+
}
75+
76+
func apiRequestsLeave() {
77+
<-apiRequestsThrottle
78+
}
79+
5380
// PublicEthereumAPI provides an API to access Ethereum related information.
5481
// It offers only methods that operate on public data that is freely available to anyone.
5582
type PublicEthereumAPI struct {
@@ -681,6 +708,20 @@ type PublicBlockChainAPI struct {
681708

682709
// NewPublicBlockChainAPI creates a new Ethereum blockchain API.
683710
func NewPublicBlockChainAPI(b Backend) *PublicBlockChainAPI {
711+
if len(params.PublicRequestsCacheLocation) > 0 {
712+
var err error
713+
apiRequestsCache, err = apiCacheOpen(params.PublicRequestsCacheLocation)
714+
if err != nil {
715+
panic(err)
716+
}
717+
}
718+
apiRequestsThrottle = make(chan struct{}, params.MaxPublicRequests)
719+
tokens := runtime.NumCPU() * 8 / 10
720+
if tokens < 4 {
721+
tokens = 4
722+
}
723+
apiRequestsTokens = make(chan struct{}, tokens)
724+
684725
return &PublicBlockChainAPI{b}
685726
}
686727

@@ -701,6 +742,21 @@ func (s *PublicBlockChainAPI) BlockNumber() hexutil.Uint64 {
701742

702743
// GetBlockReceipts returns all the transaction receipts for the given block hash.
703744
func (s *PublicBlockChainAPI) GetReceiptsByHash(ctx context.Context, blockHash common.Hash) ([]map[string]interface{}, error) {
745+
apiRequestsEnter()
746+
defer apiRequestsLeave()
747+
748+
select {
749+
case <-ctx.Done():
750+
return nil, io.EOF
751+
default:
752+
}
753+
754+
if apiRequestsCache != nil {
755+
if fields, err := apiCacheGetReceipts(apiRequestsCache, blockHash.Bytes()); err == nil {
756+
log.Debug("API Cache", "found receipts", blockHash)
757+
return fields, nil
758+
}
759+
}
704760

705761
block, err1 := s.b.BlockByHash(ctx, blockHash)
706762
if block == nil && err1 == nil {
@@ -723,6 +779,11 @@ func (s *PublicBlockChainAPI) GetReceiptsByHash(ctx context.Context, blockHash c
723779
fieldsList := make([]map[string]interface{}, 0, len(receipts))
724780

725781
for index, receipt := range receipts {
782+
select {
783+
case <-ctx.Done():
784+
return nil, io.EOF
785+
default:
786+
}
726787

727788
bigblock := new(big.Int).SetUint64(block.NumberU64())
728789
signer := types.MakeSigner(s.b.ChainConfig(), bigblock)
@@ -770,6 +831,9 @@ func (s *PublicBlockChainAPI) GetReceiptsByHash(ctx context.Context, blockHash c
770831

771832
fieldsList = append(fieldsList, fields)
772833
}
834+
if apiRequestsCache != nil {
835+
apiCachePutReceipts(apiRequestsCache, blockHash.Bytes(), fieldsList)
836+
}
773837
return fieldsList, nil
774838
}
775839

@@ -884,6 +948,9 @@ func (s *PublicBlockChainAPI) GetHeaderByHash(ctx context.Context, hash common.H
884948
// - When fullTx is true all transactions in the block are returned, otherwise
885949
// only the transaction hash is returned.
886950
func (s *PublicBlockChainAPI) GetBlockByNumber(ctx context.Context, number rpc.BlockNumber, fullTx bool) (map[string]interface{}, error) {
951+
apiRequestsEnter()
952+
defer apiRequestsLeave()
953+
887954
block, err := s.b.BlockByNumber(ctx, number)
888955
if block != nil && err == nil {
889956
response, err := s.rpcMarshalBlock(ctx, block, true, fullTx)
@@ -901,6 +968,9 @@ func (s *PublicBlockChainAPI) GetBlockByNumber(ctx context.Context, number rpc.B
901968
// GetBlockByHash returns the requested block. When fullTx is true all transactions in the block are returned in full
902969
// detail, otherwise only the transaction hash is returned.
903970
func (s *PublicBlockChainAPI) GetBlockByHash(ctx context.Context, hash common.Hash, fullTx bool) (map[string]interface{}, error) {
971+
apiRequestsEnter()
972+
defer apiRequestsLeave()
973+
904974
block, err := s.b.BlockByHash(ctx, hash)
905975
if block != nil {
906976
return s.rpcMarshalBlock(ctx, block, true, fullTx)
@@ -910,6 +980,9 @@ func (s *PublicBlockChainAPI) GetBlockByHash(ctx context.Context, hash common.Ha
910980

911981
// GetUncleByBlockNumberAndIndex returns the uncle block for the given block hash and index.
912982
func (s *PublicBlockChainAPI) GetUncleByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index hexutil.Uint) (map[string]interface{}, error) {
983+
apiRequestsEnter()
984+
defer apiRequestsLeave()
985+
913986
block, err := s.b.BlockByNumber(ctx, blockNr)
914987
if block != nil {
915988
uncles := block.Uncles()
@@ -925,6 +998,9 @@ func (s *PublicBlockChainAPI) GetUncleByBlockNumberAndIndex(ctx context.Context,
925998

926999
// GetUncleByBlockHashAndIndex returns the uncle block for the given block hash and index.
9271000
func (s *PublicBlockChainAPI) GetUncleByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index hexutil.Uint) (map[string]interface{}, error) {
1001+
apiRequestsEnter()
1002+
defer apiRequestsLeave()
1003+
9281004
block, err := s.b.BlockByHash(ctx, blockHash)
9291005
if block != nil {
9301006
uncles := block.Uncles()
@@ -940,6 +1016,9 @@ func (s *PublicBlockChainAPI) GetUncleByBlockHashAndIndex(ctx context.Context, b
9401016

9411017
// GetUncleCountByBlockNumber returns number of uncles in the block for the given block number
9421018
func (s *PublicBlockChainAPI) GetUncleCountByBlockNumber(ctx context.Context, blockNr rpc.BlockNumber) *hexutil.Uint {
1019+
apiRequestsEnter()
1020+
defer apiRequestsLeave()
1021+
9431022
if block, _ := s.b.BlockByNumber(ctx, blockNr); block != nil {
9441023
n := hexutil.Uint(len(block.Uncles()))
9451024
return &n
@@ -949,6 +1028,9 @@ func (s *PublicBlockChainAPI) GetUncleCountByBlockNumber(ctx context.Context, bl
9491028

9501029
// GetUncleCountByBlockHash returns number of uncles in the block for the given block hash
9511030
func (s *PublicBlockChainAPI) GetUncleCountByBlockHash(ctx context.Context, blockHash common.Hash) *hexutil.Uint {
1031+
apiRequestsEnter()
1032+
defer apiRequestsLeave()
1033+
9521034
if block, _ := s.b.BlockByHash(ctx, blockHash); block != nil {
9531035
n := hexutil.Uint(len(block.Uncles()))
9541036
return &n
@@ -1299,7 +1381,20 @@ func RPCMarshalHeader(head *types.Header) map[string]interface{} {
12991381
// RPCMarshalBlock converts the given block to the RPC output which depends on fullTx. If inclTx is true transactions are
13001382
// returned. When fullTx is true the returned block contains full transaction details, otherwise it will only contain
13011383
// transaction hashes.
1302-
func RPCMarshalBlock(block *types.Block, inclTx bool, fullTx bool, config *params.ChainConfig) (map[string]interface{}, error) {
1384+
func RPCMarshalBlock(ctx context.Context, block *types.Block, inclTx bool, fullTx bool, config *params.ChainConfig) (map[string]interface{}, error) {
1385+
select {
1386+
case <-ctx.Done():
1387+
return nil, io.EOF
1388+
default:
1389+
}
1390+
1391+
if fullTx && apiRequestsCache != nil {
1392+
if fields, err := apiCacheGetBlock(apiRequestsCache, block.Hash().Bytes()); err == nil {
1393+
log.Debug("API Cache", "found block", block.Number())
1394+
return fields, nil
1395+
}
1396+
}
1397+
13031398
fields := RPCMarshalHeader(block.Header())
13041399
fields["size"] = hexutil.Uint64(block.Size())
13051400

@@ -1314,11 +1409,33 @@ func RPCMarshalBlock(block *types.Block, inclTx bool, fullTx bool, config *param
13141409
}
13151410
txs := block.Transactions()
13161411
transactions := make([]interface{}, len(txs))
1412+
var wg sync.WaitGroup
13171413
var err error
13181414
for i, tx := range txs {
1319-
if transactions[i], err = formatTx(tx); err != nil {
1320-
return nil, err
1321-
}
1415+
wg.Add(1)
1416+
go func(ii int, itx *types.Transaction) {
1417+
apiRequestsTokens <- struct{}{}
1418+
defer func() {
1419+
wg.Done()
1420+
<-apiRequestsTokens
1421+
}()
1422+
1423+
select {
1424+
case <-ctx.Done():
1425+
err = io.EOF
1426+
return
1427+
default:
1428+
}
1429+
var err2 error
1430+
transactions[ii], err2 = formatTx(itx)
1431+
if err2 != nil {
1432+
err = err2
1433+
}
1434+
}(i, tx)
1435+
}
1436+
wg.Wait()
1437+
if err != nil {
1438+
return nil, err
13221439
}
13231440
fields["transactions"] = transactions
13241441
}
@@ -1329,6 +1446,10 @@ func RPCMarshalBlock(block *types.Block, inclTx bool, fullTx bool, config *param
13291446
}
13301447
fields["uncles"] = uncleHashes
13311448

1449+
if fullTx && apiRequestsCache != nil {
1450+
apiCachePutBlock(apiRequestsCache, block.Hash().Bytes(), fields)
1451+
}
1452+
13321453
return fields, nil
13331454
}
13341455

@@ -1343,7 +1464,7 @@ func (s *PublicBlockChainAPI) rpcMarshalHeader(ctx context.Context, header *type
13431464
// rpcMarshalBlock uses the generalized output filler, then adds the total difficulty field, which requires
13441465
// a `PublicBlockchainAPI`.
13451466
func (s *PublicBlockChainAPI) rpcMarshalBlock(ctx context.Context, b *types.Block, inclTx bool, fullTx bool) (map[string]interface{}, error) {
1346-
fields, err := RPCMarshalBlock(b, inclTx, fullTx, s.b.ChainConfig())
1467+
fields, err := RPCMarshalBlock(ctx, b, inclTx, fullTx, s.b.ChainConfig())
13471468
if err != nil {
13481469
return nil, err
13491470
}

0 commit comments

Comments
 (0)