Draft
Conversation
…he fast path - rawdbv3: add MaxWithCursor/MinWithCursor accepting a caller-provided cursor; Min/Max delegate to them. MapTxNum2BlockNumIter opens one cursor lazily and reuses it for all Min+Max calls across block changes, avoiding 2 cursor open/close per block in getLogsV3. - types/log: extract BuildTopicMap + FilterWithTopicMap so the topic map is built once per request instead of once per log batch; Filter() delegates to them. - eth_receipts: applyFiltersV3 opens one shared cursor for Min+MaxWithCursor. getLogsV3 calls BuildTopicMap once before the loop and uses TryGetCachedReceipt to skip TxnByIdxInBlock (snapshot open + RLP decode) on cache hits. - receipts_generator: receiptCache keyed by txNum (uint64) instead of txnHash, so TryGetCachedReceipt can look up receipts without resolving the hash first. Add TryGetCachedReceipt: checks receiptCache (per-tx, hot path) then receiptsCache (block-level, populated only by eth_getBlockReceipts). - cmd/scripts: add bench_eth_getlogs.py, bench_getlogs.sh, bench_stress_getlogs.sh for before/after latency and throughput benchmarking. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR optimizes the eth_getLogs RPC hot path by reducing repeated work (cursor opens, topic-map construction, and receipt retrieval) and by improving receipt caching lookups.
Changes:
- rawdbv3: add
MinWithCursor/MaxWithCursorand reuse a lazily-openedkv.MaxTxNumcursor inTxNums2BlockNumsiteration to cut cursor churn. - types/log: factor out
BuildTopicMap+FilterWithTopicMapso topic lookups are built once per request instead of per batch. - receipts: add a receipt-cache fast path keyed by
txNumto avoidTxnByIdxInBlockon cache hits; updategetLogsV3to use it.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| rpc/jsonrpc/receipts/receipts_generator.go | Switch per-tx receipt cache key to txNum and add TryGetCachedReceipt fast path. |
| rpc/jsonrpc/eth_receipts.go | Reuse kv.MaxTxNum cursor for Min/Max lookups; prebuild topic map once; use cached receipt fast path in getLogsV3. |
| rpc/jsonrpc/erigon_receipts.go | Prebuild topic map once and reuse it for log filtering. |
| execution/types/log.go | Introduce reusable topic-map builder and topic-map-based filter helper; keep Filter as wrapper. |
| db/kv/rawdbv3/txnum.go | Add MinWithCursor/MaxWithCursor and reuse one cursor inside MapTxNum2BlockNumIter. |
| db/kv/rawdbv3/txnum_test.go | Add benchmark covering cursor open/close overhead for iterator block changes. |
| cmd/scripts/bench_stress_getlogs.sh | Add script to run/compare rpc_perf stress tests for eth_getLogs. |
| cmd/scripts/bench_getlogs.sh | Add wrapper script for the direct HTTP benchmarking tool. |
| cmd/scripts/bench_eth_getlogs.py | Add direct HTTP benchmark tool measuring cold/warm eth_getLogs latencies across scenarios. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…move stress script - bench_eth_getlogs.py: fix from=end-n+1 so n_blocks matches the inclusive range - BuildTopicMap: skip allocation for wildcard positions (len(v)==0 → nil entry) - log_test.go: add TestFilterWithTopicMapEquivalence — equivalence of Filter vs FilterWithTopicMap(BuildTopicMap) across wildcard, exact, alternatives, maxLogs cases - receipts: add TestTryGetCachedReceipt covering blockHash hit, stale reorg miss, postState mismatch, receiptsCache txIndex fallback, txIndex<0 and out-of-bounds - remove bench_stress_getlogs.sh (rpc_perf launched directly) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- add t.Parallel() to all subtests (each creates its own generator, no shared state) - switch to testify require assertions to match handler_test.go style Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The changes are:
rawdbv3: add MaxWithCursor/MinWithCursor accepting a caller-provided cursor; Min/Max delegate to them. MapTxNum2BlockNumIter opens one cursor lazily and reuses it for all Min+Max calls across block changes, avoiding 2 cursor open/close per block in getLogsV3.
types/log: extract BuildTopicMap + FilterWithTopicMap so the topic map is built once per request instead of once per log batch; Filter() delegates to them.
eth_receipts: getLogsV3 calls BuildTopicMap once before the loop and uses TryGetCachedReceipt to skip TxnByIdxInBlock (snapshot open + RLP decode) on cache hits.
receipts_generator: receiptCache keyed by txNum (uint64) instead of txnHash, so TryGetCachedReceipt can look up receipts without resolving the hash first.
Performance misure:
Main SW
./build/bin/rpc_perf -p perf/pattern/mainnet/stress_test_eth_getLogs_15M.tar -t 10000:15,20000:15,30000:15 -r 5 -y eth_getLogs
Performance Test started
Test repetitions: 5 on sequence: 10000:15,20000:15,30000:15 for pattern: perf/pattern/mainnet/stress_test_eth_getLogs_15M.tar
Test on port: http://localhost:8545
[1.1] rpcdaemon: executes test qps: 10000 time: 15 -> success= 98.07% lat=[p50= 272µs p90= 17.54ms p95=105.63ms p99=453.71ms max= 2.79s] error=Post "http://localhost:8545": dial tcp 127.0.0.1:8545: connect: connection refused (x1)
[1.2] rpcdaemon: executes test qps: 10000 time: 15 -> success=100.00% lat=[max=166.60ms]
[1.3] rpcdaemon: executes test qps: 10000 time: 15 -> success=100.00% lat=[max=145.91ms]
[1.4] rpcdaemon: executes test qps: 10000 time: 15 -> success=100.00% lat=[max=142.72ms]
[1.5] rpcdaemon: executes test qps: 10000 time: 15 -> success=100.00% lat=[max=155.32ms]
[2.1] rpcdaemon: executes test qps: 20000 time: 15 -> success=100.00% lat=[max=166.88ms]
[2.2] rpcdaemon: executes test qps: 20000 time: 15 -> success=100.00% lat=[max=167.75ms]
[2.3] rpcdaemon: executes test qps: 20000 time: 15 -> success=100.00% lat=[max=156.63ms]
[2.4] rpcdaemon: executes test qps: 20000 time: 15 -> success=100.00% lat=[max=153.00ms]
[2.5] rpcdaemon: executes test qps: 20000 time: 15 -> success=100.00% lat=[max=124.04ms]
[3.1] rpcdaemon: executes test qps: 30000 time: 15 -> success=100.00% lat=[max=176.64ms]
[3.2] rpcdaemon: executes test qps: 30000 time: 15 -> success= 99.65% lat=[max=199.70ms] error=503 Service Unavailable (x1)
[3.3] rpcdaemon: executes test qps: 30000 time: 15 -> success=100.00% lat=[max=219.24ms]
[3.4] rpcdaemon: executes test qps: 30000 time: 15 -> success=100.00% lat=[max=204.67ms]
[3.5] rpcdaemon: executes test qps: 30000 time: 15 -> success=100.00% lat=[max=176.81ms]
Current Branch
./build/bin/rpc_perf -p perf/pattern/mainnet/stress_test_eth_getLogs_15M.tar -t 10000:15,20000:15,30000:15 -r 5 -y eth_getLogs -P
Performance Test started
Test repetitions: 5 on sequence: 10000:15,20000:15,30000:15 for pattern: perf/pattern/mainnet/stress_test_eth_getLogs_15M.tar
Test on port: http://localhost:8545
[1.1] rpcdaemon: executes test qps: 10000 time: 15 -> success=100.00% lat=[max=576.59ms]
[1.2] rpcdaemon: executes test qps: 10000 time: 15 -> success=100.00% lat=[max=122.24ms]
[1.3] rpcdaemon: executes test qps: 10000 time: 15 -> success=100.00% lat=[max= 86.22ms]
[1.4] rpcdaemon: executes test qps: 10000 time: 15 -> success=100.00% lat=[max=120.32ms]
[1.5] rpcdaemon: executes test qps: 10000 time: 15 -> success=100.00% lat=[max= 87.61ms]
[2.1] rpcdaemon: executes test qps: 20000 time: 15 -> success=100.00% lat=[max=100.07ms]
[2.2] rpcdaemon: executes test qps: 20000 time: 15 -> success=100.00% lat=[max=139.87ms]
[2.3] rpcdaemon: executes test qps: 20000 time: 15 -> success=100.00% lat=[max=116.74ms]
[2.4] rpcdaemon: executes test qps: 20000 time: 15 -> success=100.00% lat=[max=117.39ms]
[2.5] rpcdaemon: executes test qps: 20000 time: 15 -> success=100.00% lat=[max=119.72ms]
[3.1] rpcdaemon: executes test qps: 30000 time: 15 -> success=100.00% lat=[max=134.90ms]
[3.2] rpcdaemon: executes test qps: 30000 time: 15 -> success=100.00% lat=[max=124.48ms]
[3.3] rpcdaemon: executes test qps: 30000 time: 15 -> success=100.00% lat=[max=145.09ms]
[3.4] rpcdaemon: executes test qps: 30000 time: 15 -> success=100.00% lat=[max=125.88ms]
[3.5] rpcdaemon: executes test qps: 30000 time: 15 -> success=100.00% lat=[max=141.82ms]