Skip to content

Commit

Permalink
RPC: Receipts LRU cache (#10112)
Browse files Browse the repository at this point in the history
for #10099
for things like `eth_getTransactionReceipt`,
`ots_searchTransactionsAfter`, etc...

Also moved:
- moved `api.chainConfig()` inside `api.getReceipts()`
- switched `ots` to use blocks/receipts lru
- switched price oracle to use blocks/receipts
  • Loading branch information
AskAlexSharov authored Apr 29, 2024
1 parent a12a99c commit 83c95ba
Show file tree
Hide file tree
Showing 10 changed files with 86 additions and 55 deletions.
9 changes: 2 additions & 7 deletions turbo/jsonrpc/erigon_receipts.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,14 @@ func (api *ErigonImpl) GetLogsByHash(ctx context.Context, hash common.Hash) ([][
}
defer tx.Rollback()

chainConfig, err := api.chainConfig(ctx, tx)
if err != nil {
return nil, err
}

block, err := api.blockByHashWithSenders(ctx, tx, hash)
if err != nil {
return nil, err
}
if block == nil {
return nil, nil
}
receipts, err := api.getReceipts(ctx, tx, chainConfig, block, block.Body().SendersFromTxs())
receipts, err := api.getReceipts(ctx, tx, block, block.Body().SendersFromTxs())
if err != nil {
return nil, fmt.Errorf("getReceipts error: %w", err)
}
Expand Down Expand Up @@ -426,7 +421,7 @@ func (api *ErigonImpl) GetBlockReceiptsByBlockHash(ctx context.Context, cannonic
if err != nil {
return nil, err
}
receipts, err := api.getReceipts(ctx, tx, chainConfig, block, block.Body().SendersFromTxs())
receipts, err := api.getReceipts(ctx, tx, block, block.Body().SendersFromTxs())
if err != nil {
return nil, fmt.Errorf("getReceipts error: %w", err)
}
Expand Down
33 changes: 28 additions & 5 deletions turbo/jsonrpc/eth_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,11 @@ type EthAPI interface {
}

type BaseAPI struct {
stateCache kvcache.Cache // thread-safe
blocksLRU *lru.Cache[common.Hash, *types.Block] // thread-safe
// all caches are thread-safe
stateCache kvcache.Cache
blocksLRU *lru.Cache[common.Hash, *types.Block]
receiptsCache *lru.Cache[common.Hash, []*types.Receipt]

filters *rpchelper.Filters
_chainConfig atomic.Pointer[chain.Config]
_genesis atomic.Pointer[types.Block]
Expand All @@ -127,16 +130,36 @@ type BaseAPI struct {
}

func NewBaseApi(f *rpchelper.Filters, stateCache kvcache.Cache, blockReader services.FullBlockReader, agg *libstate.Aggregator, singleNodeMode bool, evmCallTimeout time.Duration, engine consensus.EngineReader, dirs datadir.Dirs) *BaseAPI {
blocksLRUSize := 128 // ~32Mb
var (
blocksLRUSize = 128 // ~32Mb
receiptsCacheLimit = 32
)
// if RPCDaemon deployed as independent process: increase cache sizes
if !singleNodeMode {
blocksLRUSize = 512
blocksLRUSize *= 5
receiptsCacheLimit *= 5
}
blocksLRU, err := lru.New[common.Hash, *types.Block](blocksLRUSize)
if err != nil {
panic(err)
}
receiptsCache, err := lru.New[common.Hash, []*types.Receipt](receiptsCacheLimit)
if err != nil {
panic(err)
}

return &BaseAPI{filters: f, stateCache: stateCache, blocksLRU: blocksLRU, _blockReader: blockReader, _txnReader: blockReader, _agg: agg, evmCallTimeout: evmCallTimeout, _engine: engine, dirs: dirs}
return &BaseAPI{
filters: f,
stateCache: stateCache,
blocksLRU: blocksLRU,
receiptsCache: receiptsCache,
_blockReader: blockReader,
_txnReader: blockReader,
_agg: agg,
evmCallTimeout: evmCallTimeout,
_engine: engine,
dirs: dirs,
}
}

func (api *BaseAPI) chainConfig(ctx context.Context, tx kv.Tx) (*chain.Config, error) {
Expand Down
25 changes: 18 additions & 7 deletions turbo/jsonrpc/eth_receipts.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,22 @@ import (
"github.com/ledgerwatch/erigon/turbo/transactions"
)

func (api *BaseAPI) getReceipts(ctx context.Context, tx kv.Tx, chainConfig *chain.Config, block *types.Block, senders []common.Address) (types.Receipts, error) {
if cached := rawdb.ReadReceipts(tx, block, senders); cached != nil {
return cached, nil
// getReceipts - checking in-mem cache, or else fallback to db, or else fallback to re-exec of block to re-gen receipts
func (api *BaseAPI) getReceipts(ctx context.Context, tx kv.Tx, block *types.Block, senders []common.Address) (types.Receipts, error) {
if receipts, ok := api.receiptsCache.Get(block.Hash()); ok {
return receipts, nil
}

if receipts := rawdb.ReadReceipts(tx, block, senders); receipts != nil {
api.receiptsCache.Add(block.Hash(), receipts)
return receipts, nil
}

engine := api.engine()
chainConfig, err := api.chainConfig(ctx, tx)
if err != nil {
return nil, err
}

_, _, _, ibs, _, err := transactions.ComputeTxEnv(ctx, engine, block, chainConfig, api._blockReader, tx, 0, api.historyV3(tx))
if err != nil {
Expand Down Expand Up @@ -71,6 +82,7 @@ func (api *BaseAPI) getReceipts(ctx context.Context, tx kv.Tx, chainConfig *chai
receipts[i] = receipt
}

api.receiptsCache.Add(block.Hash(), receipts)
return receipts, nil
}

Expand All @@ -86,11 +98,10 @@ func (api *APIImpl) GetLogs(ctx context.Context, crit filters.FilterCriteria) (t
defer tx.Rollback()

if crit.BlockHash != nil {
block, err := api._blockReader.BlockByHash(ctx, tx, *crit.BlockHash)
block, err := api.blockByHashWithSenders(ctx, tx, *crit.BlockHash)
if err != nil {
return nil, err
}

if block == nil {
return nil, fmt.Errorf("block not found: %x", *crit.BlockHash)
}
Expand Down Expand Up @@ -648,7 +659,7 @@ func (api *APIImpl) GetTransactionReceipt(ctx context.Context, txnHash common.Ha
borTx = types.NewBorTransaction()
}
}
receipts, err := api.getReceipts(ctx, tx, cc, block, block.Body().SendersFromTxs())
receipts, err := api.getReceipts(ctx, tx, block, block.Body().SendersFromTxs())
if err != nil {
return nil, fmt.Errorf("getReceipts error: %w", err)
}
Expand Down Expand Up @@ -694,7 +705,7 @@ func (api *APIImpl) GetBlockReceipts(ctx context.Context, numberOrHash rpc.Block
if err != nil {
return nil, err
}
receipts, err := api.getReceipts(ctx, tx, chainConfig, block, block.Body().SendersFromTxs())
receipts, err := api.getReceipts(ctx, tx, block, block.Body().SendersFromTxs())
if err != nil {
return nil, fmt.Errorf("getReceipts error: %w", err)
}
Expand Down
29 changes: 8 additions & 21 deletions turbo/jsonrpc/eth_system.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,12 +111,7 @@ func (api *APIImpl) GasPrice(ctx context.Context) (*hexutil.Big, error) {
return nil, err
}
defer tx.Rollback()
cc, err := api.chainConfig(ctx, tx)
if err != nil {
return nil, err
}

oracle := gasprice.NewOracle(NewGasPriceOracleBackend(tx, cc, api.BaseAPI), ethconfig.Defaults.GPO, api.gasCache)
oracle := gasprice.NewOracle(NewGasPriceOracleBackend(tx, api.BaseAPI), ethconfig.Defaults.GPO, api.gasCache)
tipcap, err := oracle.SuggestTipCap(ctx)
gasResult := big.NewInt(0)

Expand All @@ -138,11 +133,7 @@ func (api *APIImpl) MaxPriorityFeePerGas(ctx context.Context) (*hexutil.Big, err
return nil, err
}
defer tx.Rollback()
cc, err := api.chainConfig(ctx, tx)
if err != nil {
return nil, err
}
oracle := gasprice.NewOracle(NewGasPriceOracleBackend(tx, cc, api.BaseAPI), ethconfig.Defaults.GPO, api.gasCache)
oracle := gasprice.NewOracle(NewGasPriceOracleBackend(tx, api.BaseAPI), ethconfig.Defaults.GPO, api.gasCache)
tipcap, err := oracle.SuggestTipCap(ctx)
if err != nil {
return nil, err
Expand All @@ -163,11 +154,7 @@ func (api *APIImpl) FeeHistory(ctx context.Context, blockCount rpc.DecimalOrHex,
return nil, err
}
defer tx.Rollback()
cc, err := api.chainConfig(ctx, tx)
if err != nil {
return nil, err
}
oracle := gasprice.NewOracle(NewGasPriceOracleBackend(tx, cc, api.BaseAPI), ethconfig.Defaults.GPO, api.gasCache)
oracle := gasprice.NewOracle(NewGasPriceOracleBackend(tx, api.BaseAPI), ethconfig.Defaults.GPO, api.gasCache)

oldest, reward, baseFee, gasUsed, err := oracle.FeeHistory(ctx, int(blockCount), lastBlock, rewardPercentiles)
if err != nil {
Expand Down Expand Up @@ -197,12 +184,11 @@ func (api *APIImpl) FeeHistory(ctx context.Context, blockCount rpc.DecimalOrHex,

type GasPriceOracleBackend struct {
tx kv.Tx
cc *chain.Config
baseApi *BaseAPI
}

func NewGasPriceOracleBackend(tx kv.Tx, cc *chain.Config, baseApi *BaseAPI) *GasPriceOracleBackend {
return &GasPriceOracleBackend{tx: tx, cc: cc, baseApi: baseApi}
func NewGasPriceOracleBackend(tx kv.Tx, baseApi *BaseAPI) *GasPriceOracleBackend {
return &GasPriceOracleBackend{tx: tx, baseApi: baseApi}
}

func (b *GasPriceOracleBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) {
Expand All @@ -219,10 +205,11 @@ func (b *GasPriceOracleBackend) BlockByNumber(ctx context.Context, number rpc.Bl
return b.baseApi.blockByRPCNumber(ctx, number, b.tx)
}
func (b *GasPriceOracleBackend) ChainConfig() *chain.Config {
return b.cc
cc, _ := b.baseApi.chainConfig(context.Background(), b.tx)
return cc
}
func (b *GasPriceOracleBackend) GetReceipts(ctx context.Context, block *types.Block) (types.Receipts, error) {
return rawdb.ReadReceipts(b.tx, block, nil), nil
return b.baseApi.getReceipts(ctx, b.tx, block, nil)
}
func (b *GasPriceOracleBackend) PendingBlockAndReceipts() (*types.Block, types.Receipts) {
return nil, nil
Expand Down
12 changes: 9 additions & 3 deletions turbo/jsonrpc/graphql_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ func (api *GraphQLAPIImpl) GetBlockDetails(ctx context.Context, blockNumber rpc.
return nil, err
}

receipts, err := api.getReceipts(ctx, tx, chainConfig, block, senders)
receipts, err := api.getReceipts(ctx, tx, block, senders)
if err != nil {
return nil, fmt.Errorf("getReceipts error: %w", err)
}
Expand Down Expand Up @@ -107,8 +107,14 @@ func (api *GraphQLAPIImpl) getBlockWithSenders(ctx context.Context, number rpc.B
return nil, nil, err
}

block, senders, err := api._blockReader.BlockWithSenders(ctx, tx, blockHash, blockHeight)
return block, senders, err
block, err := api.blockWithSenders(ctx, tx, blockHash, blockHeight)
if err != nil {
return nil, nil, err
}
if block == nil {
return nil, nil, nil
}
return block, block.Body().SendersFromTxs(), nil
}

func (api *GraphQLAPIImpl) delegateGetBlockByNumber(tx kv.Tx, b *types.Block, number rpc.BlockNumber, inclTx bool) (map[string]interface{}, error) {
Expand Down
12 changes: 9 additions & 3 deletions turbo/jsonrpc/otterscan_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -573,8 +573,14 @@ func (api *OtterscanAPIImpl) getBlockWithSenders(ctx context.Context, number rpc
return nil, nil, err
}

block, senders, err := api._blockReader.BlockWithSenders(ctx, tx, hash, n)
return block, senders, err
block, err := api.blockWithSenders(ctx, tx, hash, n)
if err != nil {
return nil, nil, err
}
if block == nil {
return nil, nil, nil
}
return block, block.Body().SendersFromTxs(), nil
}

func (api *OtterscanAPIImpl) GetBlockTransactions(ctx context.Context, number rpc.BlockNumber, pageNumber uint8, pageSize uint8) (map[string]interface{}, error) {
Expand Down Expand Up @@ -603,7 +609,7 @@ func (api *OtterscanAPIImpl) GetBlockTransactions(ctx context.Context, number rp
}

// Receipts
receipts, err := api.getReceipts(ctx, tx, chainConfig, b, senders)
receipts, err := api.getReceipts(ctx, tx, b, senders)
if err != nil {
return nil, fmt.Errorf("getReceipts error: %v", err)
}
Expand Down
6 changes: 3 additions & 3 deletions turbo/jsonrpc/otterscan_block_details.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ func (api *OtterscanAPIImpl) GetBlockDetailsByHash(ctx context.Context, hash com
if blockNumber == nil {
return nil, fmt.Errorf("couldn't find block number for hash %v", hash.Bytes())
}
b, senders, err := api._blockReader.BlockWithSenders(ctx, tx, hash, *blockNumber)
b, err := api.blockWithSenders(ctx, tx, hash, *blockNumber)
if err != nil {
return nil, err
}
Expand All @@ -53,7 +53,7 @@ func (api *OtterscanAPIImpl) GetBlockDetailsByHash(ctx context.Context, hash com
}
number := rpc.BlockNumber(b.Number().Int64())

return api.getBlockDetailsImpl(ctx, tx, b, number, senders)
return api.getBlockDetailsImpl(ctx, tx, b, number, b.Body().SendersFromTxs())
}

func (api *OtterscanAPIImpl) getBlockDetailsImpl(ctx context.Context, tx kv.Tx, b *types.Block, number rpc.BlockNumber, senders []common.Address) (map[string]interface{}, error) {
Expand All @@ -70,7 +70,7 @@ func (api *OtterscanAPIImpl) getBlockDetailsImpl(ctx context.Context, tx kv.Tx,
if err != nil {
return nil, err
}
receipts, err := api.getReceipts(ctx, tx, chainConfig, b, senders)
receipts, err := api.getReceipts(ctx, tx, b, senders)
if err != nil {
return nil, fmt.Errorf("getReceipts error: %v", err)
}
Expand Down
8 changes: 5 additions & 3 deletions turbo/jsonrpc/otterscan_search_trace.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon-lib/kv"
"github.com/ledgerwatch/erigon/core"
"github.com/ledgerwatch/erigon/core/rawdb"
"github.com/ledgerwatch/erigon/core/state"
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/core/vm"
Expand Down Expand Up @@ -48,7 +47,7 @@ func (api *OtterscanAPIImpl) traceBlock(dbtx kv.Tx, ctx context.Context, blockNu
return false, nil, err
}

block, senders, err := api._blockReader.BlockWithSenders(ctx, dbtx, blockHash, blockNum)
block, err := api.blockWithSenders(ctx, dbtx, blockHash, blockNum)
if err != nil {
return false, nil, err
}
Expand All @@ -74,7 +73,10 @@ func (api *OtterscanAPIImpl) traceBlock(dbtx kv.Tx, ctx context.Context, blockNu
}
engine := api.engine()

blockReceipts := rawdb.ReadReceipts(dbtx, block, senders)
blockReceipts, err := api.getReceipts(ctx, dbtx, block, block.Body().SendersFromTxs())
if err != nil {
return false, nil, err
}
header := block.Header()
rules := chainConfig.Rules(block.NumberU64(), header.Time)
found := false
Expand Down
3 changes: 2 additions & 1 deletion turbo/jsonrpc/otterscan_transaction_by_sender_and_nonce.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,10 +276,11 @@ func (api *OtterscanAPIImpl) findNonce(ctx context.Context, tx kv.Tx, addr commo
if err != nil {
return false, common.Hash{}, err
}
block, senders, err := api._blockReader.BlockWithSenders(ctx, tx, hash, blockNum)
block, err := api.blockWithSenders(ctx, tx, hash, blockNum)
if err != nil {
return false, common.Hash{}, err
}
senders := block.Body().SendersFromTxs()

txs := block.Transactions()
for i, s := range senders {
Expand Down
4 changes: 2 additions & 2 deletions turbo/jsonrpc/overlay_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,7 @@ func (api *OverlayAPIImpl) replayBlock(ctx context.Context, blockNum uint64, sta
gp := new(core.GasPool).AddGas(math.MaxUint64).AddBlobGas(math.MaxUint64)
vmConfig := vm.Config{Debug: false}
evm = vm.NewEVM(blockCtx, evmtypes.TxContext{}, statedb, chainConfig, vmConfig)
receipts, err := api.getReceipts(ctx, tx, chainConfig, block, block.Body().SendersFromTxs())
receipts, err := api.getReceipts(ctx, tx, block, block.Body().SendersFromTxs())
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -566,7 +566,7 @@ func (api *OverlayAPIImpl) replayBlock(ctx context.Context, blockNum uint64, sta
func getBeginEnd(ctx context.Context, tx kv.Tx, api *OverlayAPIImpl, crit filters.FilterCriteria) (uint64, uint64, error) {
var begin, end uint64
if crit.BlockHash != nil {
block, err := api._blockReader.BlockByHash(ctx, tx, *crit.BlockHash)
block, err := api.blockByHashWithSenders(ctx, tx, *crit.BlockHash)
if err != nil {
return 0, 0, err
}
Expand Down

0 comments on commit 83c95ba

Please sign in to comment.