Skip to content

fix: consistently check block input cache before checking hot db for blocks - #8823

Merged
nflaig merged 3 commits into
unstablefrom
nflaig/from-cache
Feb 2, 2026
Merged

fix: consistently check block input cache before checking hot db for blocks#8823
nflaig merged 3 commits into
unstablefrom
nflaig/from-cache

Conversation

@nflaig

@nflaig nflaig commented Jan 30, 2026

Copy link
Copy Markdown
Member

Follow up on #8784 to consistently check block input cache before checking hot db for blocks.

This avoids edge cases where block is not yet persistent in db, also querying db if we can serve it from in-memory cache seems unnecessary.

This issue was caught by e2e tests

Error: Head block 30 null is not available in database
 ❯ BeaconChain.onForkChoiceFinalized src/chain/chain.ts:1351:13

@nflaig
nflaig requested a review from a team as a code owner January 30, 2026 22:24
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @nflaig, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request refines the block retrieval strategy across the beacon node by consistently prioritizing an in-memory cache before querying the persistent database. This change aims to enhance performance by reducing database I/O and improves the system's robustness by addressing edge cases where blocks might be present in memory but not yet fully persisted, resolving an issue identified by e2e tests.

Highlights

  • Optimized Block Retrieval Strategy: Changed multiple instances of direct database block retrieval (db.block.get) to utilize chain.getBlockByRoot, which prioritizes checking an in-memory cache (seenBlockInputCache) before falling back to the database. This reduces database load and improves responsiveness.
  • Enhanced State Regeneration Module: The StateRegenerator now incorporates the seenBlockInputCache to fetch blocks, ensuring that recently seen blocks are retrieved from memory rather than always querying the database during state regeneration processes.
  • Corrected Cache Pruning Logic: Fixed a bug in the SeenBlockInput cache's pruning mechanism. The cache now correctly sorts and removes the oldest blocks first when exceeding its maximum size, ensuring that the most recent and relevant blocks are retained.
  • Improved Error Handling: Updated an error message in BeaconChain.onForkChoiceFinalized to accurately reflect that a head block might be unavailable in either the cache or the database, providing clearer diagnostic information.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request effectively addresses the issue of inconsistently checking the block input cache. By replacing direct database lookups with calls to chain.getBlockByRoot or implementing a cache-first logic in StateRegenerator, the changes ensure that the in-memory cache is always consulted before querying the database. This not only improves performance by avoiding unnecessary DB access but also resolves edge cases where a block might not yet be persisted. Additionally, the PR includes a crucial bug fix in SeenBlockInput's pruning logic, ensuring the cache evicts the oldest blocks as intended. The changes are well-implemented and improve the robustness and efficiency of block handling.


if (itemsToDelete > 0) {
const sorted = [...this.blockInputs.entries()].sort((a, b) => b[1].slot - a[1].slot);
const sorted = [...this.blockInputs.entries()].sort((a, b) => a[1].slot - b[1].slot);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This is a great catch! Sorting in descending order of slot (b[1].slot - a[1].slot) would cause the cache to prune the newest blocks first, which is incorrect for an age-based eviction strategy. Changing the sort to ascending order (a[1].slot - b[1].slot) correctly ensures that the oldest blocks (by slot) are removed when the cache is full. This is an important correctness fix for the cache pruning logic.

@github-actions

github-actions Bot commented Jan 30, 2026

Copy link
Copy Markdown
Contributor

Performance Report

✔️ no performance regression detected

Full benchmark results
Benchmark suite Current: 5fec6a0 Previous: 9defa5c Ratio
getPubkeys - index2pubkey - req 1000 vs - 250000 vc 1.3130 ms/op 1.2241 ms/op 1.07
getPubkeys - validatorsArr - req 1000 vs - 250000 vc 40.210 us/op 39.054 us/op 1.03
BLS verify - blst 822.47 us/op 987.45 us/op 0.83
BLS verifyMultipleSignatures 3 - blst 1.1984 ms/op 1.2164 ms/op 0.99
BLS verifyMultipleSignatures 8 - blst 1.6868 ms/op 1.8842 ms/op 0.90
BLS verifyMultipleSignatures 32 - blst 4.9811 ms/op 5.7482 ms/op 0.87
BLS verifyMultipleSignatures 64 - blst 9.2789 ms/op 11.020 ms/op 0.84
BLS verifyMultipleSignatures 128 - blst 27.013 ms/op 17.445 ms/op 1.55
BLS deserializing 10000 signatures 710.46 ms/op 700.35 ms/op 1.01
BLS deserializing 100000 signatures 7.2900 s/op 6.9511 s/op 1.05
BLS verifyMultipleSignatures - same message - 3 - blst 862.59 us/op 996.23 us/op 0.87
BLS verifyMultipleSignatures - same message - 8 - blst 1.0076 ms/op 1.6256 ms/op 0.62
BLS verifyMultipleSignatures - same message - 32 - blst 1.7186 ms/op 1.7137 ms/op 1.00
BLS verifyMultipleSignatures - same message - 64 - blst 2.6387 ms/op 2.7576 ms/op 0.96
BLS verifyMultipleSignatures - same message - 128 - blst 4.4437 ms/op 4.5272 ms/op 0.98
BLS aggregatePubkeys 32 - blst 19.797 us/op 19.121 us/op 1.04
BLS aggregatePubkeys 128 - blst 70.549 us/op 66.715 us/op 1.06
getSlashingsAndExits - default max 71.181 us/op 72.147 us/op 0.99
getSlashingsAndExits - 2k 315.85 us/op 315.92 us/op 1.00
isKnown best case - 1 super set check 202.00 ns/op 197.00 ns/op 1.03
isKnown normal case - 2 super set checks 198.00 ns/op 198.00 ns/op 1.00
isKnown worse case - 16 super set checks 198.00 ns/op 199.00 ns/op 0.99
InMemoryCheckpointStateCache - add get delete 1.9980 us/op 1.9710 us/op 1.01
validate api signedAggregateAndProof - struct 1.3837 ms/op 2.2256 ms/op 0.62
validate gossip signedAggregateAndProof - struct 1.3846 ms/op 1.7043 ms/op 0.81
batch validate gossip attestation - vc 640000 - chunk 32 122.16 us/op 116.47 us/op 1.05
batch validate gossip attestation - vc 640000 - chunk 64 117.57 us/op 103.99 us/op 1.13
batch validate gossip attestation - vc 640000 - chunk 128 101.02 us/op 96.426 us/op 1.05
batch validate gossip attestation - vc 640000 - chunk 256 96.916 us/op 93.820 us/op 1.03
bytes32 toHexString 380.00 ns/op 372.00 ns/op 1.02
bytes32 Buffer.toString(hex) 252.00 ns/op 250.00 ns/op 1.01
bytes32 Buffer.toString(hex) from Uint8Array 339.00 ns/op 355.00 ns/op 0.95
bytes32 Buffer.toString(hex) + 0x 257.00 ns/op 235.00 ns/op 1.09
Return object 10000 times 0.23700 ns/op 0.23460 ns/op 1.01
Throw Error 10000 times 4.1698 us/op 4.1775 us/op 1.00
toHex 136.74 ns/op 123.94 ns/op 1.10
Buffer.from 139.97 ns/op 126.90 ns/op 1.10
shared Buffer 84.813 ns/op 93.085 ns/op 0.91
fastMsgIdFn sha256 / 200 bytes 1.8850 us/op 1.8570 us/op 1.02
fastMsgIdFn h32 xxhash / 200 bytes 199.00 ns/op 241.00 ns/op 0.83
fastMsgIdFn h64 xxhash / 200 bytes 267.00 ns/op 299.00 ns/op 0.89
fastMsgIdFn sha256 / 1000 bytes 6.0350 us/op 5.9730 us/op 1.01
fastMsgIdFn h32 xxhash / 1000 bytes 294.00 ns/op 285.00 ns/op 1.03
fastMsgIdFn h64 xxhash / 1000 bytes 320.00 ns/op 327.00 ns/op 0.98
fastMsgIdFn sha256 / 10000 bytes 54.340 us/op 52.693 us/op 1.03
fastMsgIdFn h32 xxhash / 10000 bytes 1.4480 us/op 1.5350 us/op 0.94
fastMsgIdFn h64 xxhash / 10000 bytes 957.00 ns/op 1.0640 us/op 0.90
send data - 1000 256B messages 16.809 ms/op 14.765 ms/op 1.14
send data - 1000 512B messages 17.515 ms/op 17.915 ms/op 0.98
send data - 1000 1024B messages 23.347 ms/op 22.479 ms/op 1.04
send data - 1000 1200B messages 22.416 ms/op 29.867 ms/op 0.75
send data - 1000 2048B messages 22.465 ms/op 28.761 ms/op 0.78
send data - 1000 4096B messages 28.022 ms/op 32.279 ms/op 0.87
send data - 1000 16384B messages 106.42 ms/op 117.44 ms/op 0.91
send data - 1000 65536B messages 322.23 ms/op 317.92 ms/op 1.01
enrSubnets - fastDeserialize 64 bits 898.00 ns/op 895.00 ns/op 1.00
enrSubnets - ssz BitVector 64 bits 349.00 ns/op 330.00 ns/op 1.06
enrSubnets - fastDeserialize 4 bits 137.00 ns/op 141.00 ns/op 0.97
enrSubnets - ssz BitVector 4 bits 354.00 ns/op 365.00 ns/op 0.97
prioritizePeers score -10:0 att 32-0.1 sync 2-0 239.97 us/op 242.77 us/op 0.99
prioritizePeers score 0:0 att 32-0.25 sync 2-0.25 268.81 us/op 275.05 us/op 0.98
prioritizePeers score 0:0 att 32-0.5 sync 2-0.5 380.27 us/op 376.36 us/op 1.01
prioritizePeers score 0:0 att 64-0.75 sync 4-0.75 712.67 us/op 694.10 us/op 1.03
prioritizePeers score 0:0 att 64-1 sync 4-1 852.57 us/op 843.68 us/op 1.01
array of 16000 items push then shift 1.6897 us/op 1.6452 us/op 1.03
LinkedList of 16000 items push then shift 7.4880 ns/op 7.6900 ns/op 0.97
array of 16000 items push then pop 80.160 ns/op 77.334 ns/op 1.04
LinkedList of 16000 items push then pop 7.2820 ns/op 7.2500 ns/op 1.00
array of 24000 items push then shift 2.4558 us/op 2.4157 us/op 1.02
LinkedList of 24000 items push then shift 15.034 ns/op 7.6280 ns/op 1.97
array of 24000 items push then pop 113.76 ns/op 107.42 ns/op 1.06
LinkedList of 24000 items push then pop 7.3700 ns/op 7.3430 ns/op 1.00
intersect bitArray bitLen 8 5.8260 ns/op 5.7950 ns/op 1.01
intersect array and set length 8 34.211 ns/op 34.072 ns/op 1.00
intersect bitArray bitLen 128 29.264 ns/op 29.165 ns/op 1.00
intersect array and set length 128 565.65 ns/op 557.40 ns/op 1.01
bitArray.getTrueBitIndexes() bitLen 128 1.0590 us/op 991.00 ns/op 1.07
bitArray.getTrueBitIndexes() bitLen 248 1.8420 us/op 1.7540 us/op 1.05
bitArray.getTrueBitIndexes() bitLen 512 3.9070 us/op 3.6630 us/op 1.07
Full columns - reconstruct all 6 blobs 364.03 us/op 351.97 us/op 1.03
Full columns - reconstruct half of the blobs out of 6 101.79 us/op 115.48 us/op 0.88
Full columns - reconstruct single blob out of 6 31.332 us/op 46.859 us/op 0.67
Half columns - reconstruct all 6 blobs 275.00 ms/op 276.97 ms/op 0.99
Half columns - reconstruct half of the blobs out of 6 138.43 ms/op 138.38 ms/op 1.00
Half columns - reconstruct single blob out of 6 56.790 ms/op 51.041 ms/op 1.11
Full columns - reconstruct all 10 blobs 282.61 us/op 423.48 us/op 0.67
Full columns - reconstruct half of the blobs out of 10 148.32 us/op 145.56 us/op 1.02
Full columns - reconstruct single blob out of 10 31.631 us/op 38.597 us/op 0.82
Half columns - reconstruct all 10 blobs 586.17 ms/op 466.99 ms/op 1.26
Half columns - reconstruct half of the blobs out of 10 339.67 ms/op 236.58 ms/op 1.44
Half columns - reconstruct single blob out of 10 52.127 ms/op 52.356 ms/op 1.00
Full columns - reconstruct all 20 blobs 619.98 us/op 888.28 us/op 0.70
Full columns - reconstruct half of the blobs out of 20 293.17 us/op 392.46 us/op 0.75
Full columns - reconstruct single blob out of 20 32.089 us/op 49.013 us/op 0.65
Half columns - reconstruct all 20 blobs 924.37 ms/op 903.97 ms/op 1.02
Half columns - reconstruct half of the blobs out of 20 464.19 ms/op 448.13 ms/op 1.04
Half columns - reconstruct single blob out of 20 52.146 ms/op 51.062 ms/op 1.02
Set add up to 64 items then delete first 2.0646 us/op 2.1079 us/op 0.98
OrderedSet add up to 64 items then delete first 3.0581 us/op 3.1061 us/op 0.98
Set add up to 64 items then delete last 2.3105 us/op 2.3519 us/op 0.98
OrderedSet add up to 64 items then delete last 3.5676 us/op 3.5588 us/op 1.00
Set add up to 64 items then delete middle 2.3773 us/op 2.3882 us/op 1.00
OrderedSet add up to 64 items then delete middle 5.1245 us/op 5.1520 us/op 0.99
Set add up to 128 items then delete first 4.8250 us/op 4.8355 us/op 1.00
OrderedSet add up to 128 items then delete first 6.9319 us/op 6.9136 us/op 1.00
Set add up to 128 items then delete last 4.7597 us/op 4.7819 us/op 1.00
OrderedSet add up to 128 items then delete last 7.0990 us/op 7.1306 us/op 1.00
Set add up to 128 items then delete middle 4.7084 us/op 4.7333 us/op 0.99
OrderedSet add up to 128 items then delete middle 13.639 us/op 13.745 us/op 0.99
Set add up to 256 items then delete first 10.170 us/op 9.9207 us/op 1.03
OrderedSet add up to 256 items then delete first 14.660 us/op 14.497 us/op 1.01
Set add up to 256 items then delete last 9.5445 us/op 9.7491 us/op 0.98
OrderedSet add up to 256 items then delete last 14.585 us/op 15.037 us/op 0.97
Set add up to 256 items then delete middle 9.5616 us/op 9.7792 us/op 0.98
OrderedSet add up to 256 items then delete middle 41.679 us/op 41.018 us/op 1.02
pass gossip attestations to forkchoice per slot 2.5405 ms/op 2.5485 ms/op 1.00
forkChoice updateHead vc 100000 bc 64 eq 0 507.35 us/op 506.29 us/op 1.00
forkChoice updateHead vc 600000 bc 64 eq 0 3.0276 ms/op 3.0243 ms/op 1.00
forkChoice updateHead vc 1000000 bc 64 eq 0 5.0320 ms/op 5.0697 ms/op 0.99
forkChoice updateHead vc 600000 bc 320 eq 0 3.0649 ms/op 3.0426 ms/op 1.01
forkChoice updateHead vc 600000 bc 1200 eq 0 3.0606 ms/op 3.0669 ms/op 1.00
forkChoice updateHead vc 600000 bc 7200 eq 0 3.5879 ms/op 3.3974 ms/op 1.06
forkChoice updateHead vc 600000 bc 64 eq 1000 3.6611 ms/op 3.4806 ms/op 1.05
forkChoice updateHead vc 600000 bc 64 eq 10000 3.7903 ms/op 3.6547 ms/op 1.04
forkChoice updateHead vc 600000 bc 64 eq 300000 9.4856 ms/op 9.3635 ms/op 1.01
computeDeltas 1400000 validators 0% inactive 15.494 ms/op 14.703 ms/op 1.05
computeDeltas 1400000 validators 10% inactive 14.440 ms/op 13.803 ms/op 1.05
computeDeltas 1400000 validators 20% inactive 13.574 ms/op 12.982 ms/op 1.05
computeDeltas 1400000 validators 50% inactive 10.881 ms/op 10.175 ms/op 1.07
computeDeltas 2100000 validators 0% inactive 23.253 ms/op 22.471 ms/op 1.03
computeDeltas 2100000 validators 10% inactive 21.813 ms/op 20.440 ms/op 1.07
computeDeltas 2100000 validators 20% inactive 20.404 ms/op 19.137 ms/op 1.07
computeDeltas 2100000 validators 50% inactive 16.276 ms/op 15.252 ms/op 1.07
altair processAttestation - 250000 vs - 7PWei normalcase 1.8997 ms/op 2.0097 ms/op 0.95
altair processAttestation - 250000 vs - 7PWei worstcase 2.7991 ms/op 2.8941 ms/op 0.97
altair processAttestation - setStatus - 1/6 committees join 119.44 us/op 120.92 us/op 0.99
altair processAttestation - setStatus - 1/3 committees join 238.03 us/op 237.62 us/op 1.00
altair processAttestation - setStatus - 1/2 committees join 333.27 us/op 334.06 us/op 1.00
altair processAttestation - setStatus - 2/3 committees join 426.08 us/op 428.99 us/op 0.99
altair processAttestation - setStatus - 4/5 committees join 599.09 us/op 580.19 us/op 1.03
altair processAttestation - setStatus - 100% committees join 975.67 us/op 698.49 us/op 1.40
altair processBlock - 250000 vs - 7PWei normalcase 3.5728 ms/op 3.6422 ms/op 0.98
altair processBlock - 250000 vs - 7PWei normalcase hashState 18.371 ms/op 20.041 ms/op 0.92
altair processBlock - 250000 vs - 7PWei worstcase 24.092 ms/op 23.620 ms/op 1.02
altair processBlock - 250000 vs - 7PWei worstcase hashState 54.453 ms/op 57.783 ms/op 0.94
phase0 processBlock - 250000 vs - 7PWei normalcase 1.4615 ms/op 1.9404 ms/op 0.75
phase0 processBlock - 250000 vs - 7PWei worstcase 18.603 ms/op 21.692 ms/op 0.86
altair processEth1Data - 250000 vs - 7PWei normalcase 368.42 us/op 414.16 us/op 0.89
getExpectedWithdrawals 250000 eb:1,eth1:1,we:0,wn:0,smpl:16 6.0040 us/op 10.942 us/op 0.55
getExpectedWithdrawals 250000 eb:0.95,eth1:0.1,we:0.05,wn:0,smpl:220 36.223 us/op 42.306 us/op 0.86
getExpectedWithdrawals 250000 eb:0.95,eth1:0.3,we:0.05,wn:0,smpl:43 11.113 us/op 12.002 us/op 0.93
getExpectedWithdrawals 250000 eb:0.95,eth1:0.7,we:0.05,wn:0,smpl:19 6.8320 us/op 7.9580 us/op 0.86
getExpectedWithdrawals 250000 eb:0.1,eth1:0.1,we:0,wn:0,smpl:1021 142.01 us/op 204.35 us/op 0.69
getExpectedWithdrawals 250000 eb:0.03,eth1:0.03,we:0,wn:0,smpl:11778 1.8528 ms/op 2.2341 ms/op 0.83
getExpectedWithdrawals 250000 eb:0.01,eth1:0.01,we:0,wn:0,smpl:16384 2.3570 ms/op 2.9217 ms/op 0.81
getExpectedWithdrawals 250000 eb:0,eth1:0,we:0,wn:0,smpl:16384 2.3659 ms/op 2.4299 ms/op 0.97
getExpectedWithdrawals 250000 eb:0,eth1:0,we:0,wn:0,nocache,smpl:16384 4.4902 ms/op 4.9277 ms/op 0.91
getExpectedWithdrawals 250000 eb:0,eth1:1,we:0,wn:0,smpl:16384 2.7309 ms/op 2.9322 ms/op 0.93
getExpectedWithdrawals 250000 eb:0,eth1:1,we:0,wn:0,nocache,smpl:16384 4.9160 ms/op 5.1593 ms/op 0.95
Tree 40 250000 create 378.17 ms/op 399.52 ms/op 0.95
Tree 40 250000 get(125000) 125.96 ns/op 129.53 ns/op 0.97
Tree 40 250000 set(125000) 1.2530 us/op 1.3394 us/op 0.94
Tree 40 250000 toArray() 14.074 ms/op 17.533 ms/op 0.80
Tree 40 250000 iterate all - toArray() + loop 15.536 ms/op 16.948 ms/op 0.92
Tree 40 250000 iterate all - get(i) 46.479 ms/op 47.009 ms/op 0.99
Array 250000 create 2.5938 ms/op 2.5287 ms/op 1.03
Array 250000 clone - spread 837.02 us/op 826.88 us/op 1.01
Array 250000 get(125000) 0.35400 ns/op 0.34600 ns/op 1.02
Array 250000 set(125000) 0.36500 ns/op 0.35300 ns/op 1.03
Array 250000 iterate all - loop 62.595 us/op 62.041 us/op 1.01
phase0 afterProcessEpoch - 250000 vs - 7PWei 42.698 ms/op 42.021 ms/op 1.02
Array.fill - length 1000000 2.9237 ms/op 2.9723 ms/op 0.98
Array push - length 1000000 11.752 ms/op 12.445 ms/op 0.94
Array.get 0.22382 ns/op 0.21728 ns/op 1.03
Uint8Array.get 0.22460 ns/op 0.22248 ns/op 1.01
phase0 beforeProcessEpoch - 250000 vs - 7PWei 17.274 ms/op 13.675 ms/op 1.26
altair processEpoch - mainnet_e81889 289.59 ms/op 254.24 ms/op 1.14
mainnet_e81889 - altair beforeProcessEpoch 16.517 ms/op 21.794 ms/op 0.76
mainnet_e81889 - altair processJustificationAndFinalization 5.7380 us/op 6.3100 us/op 0.91
mainnet_e81889 - altair processInactivityUpdates 4.0913 ms/op 3.7775 ms/op 1.08
mainnet_e81889 - altair processRewardsAndPenalties 19.011 ms/op 17.971 ms/op 1.06
mainnet_e81889 - altair processRegistryUpdates 639.00 ns/op 632.00 ns/op 1.01
mainnet_e81889 - altair processSlashings 169.00 ns/op 206.00 ns/op 0.82
mainnet_e81889 - altair processEth1DataReset 167.00 ns/op 211.00 ns/op 0.79
mainnet_e81889 - altair processEffectiveBalanceUpdates 1.9601 ms/op 2.2901 ms/op 0.86
mainnet_e81889 - altair processSlashingsReset 836.00 ns/op 787.00 ns/op 1.06
mainnet_e81889 - altair processRandaoMixesReset 1.1000 us/op 1.2930 us/op 0.85
mainnet_e81889 - altair processHistoricalRootsUpdate 172.00 ns/op 167.00 ns/op 1.03
mainnet_e81889 - altair processParticipationFlagUpdates 536.00 ns/op 543.00 ns/op 0.99
mainnet_e81889 - altair processSyncCommitteeUpdates 134.00 ns/op 143.00 ns/op 0.94
mainnet_e81889 - altair afterProcessEpoch 44.402 ms/op 44.619 ms/op 1.00
capella processEpoch - mainnet_e217614 835.27 ms/op 743.56 ms/op 1.12
mainnet_e217614 - capella beforeProcessEpoch 88.075 ms/op 60.560 ms/op 1.45
mainnet_e217614 - capella processJustificationAndFinalization 5.4890 us/op 5.3870 us/op 1.02
mainnet_e217614 - capella processInactivityUpdates 18.903 ms/op 13.825 ms/op 1.37
mainnet_e217614 - capella processRewardsAndPenalties 111.54 ms/op 102.43 ms/op 1.09
mainnet_e217614 - capella processRegistryUpdates 5.9840 us/op 5.9160 us/op 1.01
mainnet_e217614 - capella processSlashings 169.00 ns/op 164.00 ns/op 1.03
mainnet_e217614 - capella processEth1DataReset 168.00 ns/op 201.00 ns/op 0.84
mainnet_e217614 - capella processEffectiveBalanceUpdates 22.187 ms/op 14.171 ms/op 1.57
mainnet_e217614 - capella processSlashingsReset 839.00 ns/op 868.00 ns/op 0.97
mainnet_e217614 - capella processRandaoMixesReset 1.1390 us/op 1.4310 us/op 0.80
mainnet_e217614 - capella processHistoricalRootsUpdate 171.00 ns/op 185.00 ns/op 0.92
mainnet_e217614 - capella processParticipationFlagUpdates 532.00 ns/op 563.00 ns/op 0.94
mainnet_e217614 - capella afterProcessEpoch 117.29 ms/op 115.92 ms/op 1.01
phase0 processEpoch - mainnet_e58758 252.32 ms/op 223.14 ms/op 1.13
mainnet_e58758 - phase0 beforeProcessEpoch 52.353 ms/op 48.316 ms/op 1.08
mainnet_e58758 - phase0 processJustificationAndFinalization 5.7320 us/op 5.5990 us/op 1.02
mainnet_e58758 - phase0 processRewardsAndPenalties 19.354 ms/op 18.983 ms/op 1.02
mainnet_e58758 - phase0 processRegistryUpdates 2.8170 us/op 2.7260 us/op 1.03
mainnet_e58758 - phase0 processSlashings 173.00 ns/op 203.00 ns/op 0.85
mainnet_e58758 - phase0 processEth1DataReset 166.00 ns/op 220.00 ns/op 0.75
mainnet_e58758 - phase0 processEffectiveBalanceUpdates 1.1956 ms/op 1.1957 ms/op 1.00
mainnet_e58758 - phase0 processSlashingsReset 921.00 ns/op 899.00 ns/op 1.02
mainnet_e58758 - phase0 processRandaoMixesReset 1.2660 us/op 1.1760 us/op 1.08
mainnet_e58758 - phase0 processHistoricalRootsUpdate 174.00 ns/op 208.00 ns/op 0.84
mainnet_e58758 - phase0 processParticipationRecordUpdates 864.00 ns/op 850.00 ns/op 1.02
mainnet_e58758 - phase0 afterProcessEpoch 35.907 ms/op 35.245 ms/op 1.02
phase0 processEffectiveBalanceUpdates - 250000 normalcase 1.8475 ms/op 1.6794 ms/op 1.10
phase0 processEffectiveBalanceUpdates - 250000 worstcase 0.5 2.0123 ms/op 1.5361 ms/op 1.31
altair processInactivityUpdates - 250000 normalcase 12.853 ms/op 13.623 ms/op 0.94
altair processInactivityUpdates - 250000 worstcase 14.534 ms/op 12.064 ms/op 1.20
phase0 processRegistryUpdates - 250000 normalcase 4.4200 us/op 7.4610 us/op 0.59
phase0 processRegistryUpdates - 250000 badcase_full_deposits 218.01 us/op 358.40 us/op 0.61
phase0 processRegistryUpdates - 250000 worstcase 0.5 69.750 ms/op 61.756 ms/op 1.13
altair processRewardsAndPenalties - 250000 normalcase 17.845 ms/op 16.172 ms/op 1.10
altair processRewardsAndPenalties - 250000 worstcase 17.804 ms/op 15.418 ms/op 1.15
phase0 getAttestationDeltas - 250000 normalcase 6.9233 ms/op 5.5452 ms/op 1.25
phase0 getAttestationDeltas - 250000 worstcase 6.9442 ms/op 5.5742 ms/op 1.25
phase0 processSlashings - 250000 worstcase 104.17 us/op 123.11 us/op 0.85
altair processSyncCommitteeUpdates - 250000 11.371 ms/op 10.483 ms/op 1.08
BeaconState.hashTreeRoot - No change 196.00 ns/op 279.00 ns/op 0.70
BeaconState.hashTreeRoot - 1 full validator 92.563 us/op 114.28 us/op 0.81
BeaconState.hashTreeRoot - 32 full validator 1.2086 ms/op 1.0815 ms/op 1.12
BeaconState.hashTreeRoot - 512 full validator 8.3708 ms/op 8.8152 ms/op 0.95
BeaconState.hashTreeRoot - 1 validator.effectiveBalance 109.00 us/op 129.83 us/op 0.84
BeaconState.hashTreeRoot - 32 validator.effectiveBalance 1.5216 ms/op 2.6201 ms/op 0.58
BeaconState.hashTreeRoot - 512 validator.effectiveBalance 20.395 ms/op 19.832 ms/op 1.03
BeaconState.hashTreeRoot - 1 balances 81.123 us/op 83.518 us/op 0.97
BeaconState.hashTreeRoot - 32 balances 876.13 us/op 1.3318 ms/op 0.66
BeaconState.hashTreeRoot - 512 balances 6.2508 ms/op 6.3310 ms/op 0.99
BeaconState.hashTreeRoot - 250000 balances 140.67 ms/op 131.72 ms/op 1.07
aggregationBits - 2048 els - zipIndexesInBitList 21.257 us/op 20.703 us/op 1.03
regular array get 100000 times 25.192 us/op 24.311 us/op 1.04
wrappedArray get 100000 times 25.025 us/op 24.194 us/op 1.03
arrayWithProxy get 100000 times 14.921 ms/op 15.521 ms/op 0.96
ssz.Root.equals 24.339 ns/op 23.542 ns/op 1.03
byteArrayEquals 23.579 ns/op 22.885 ns/op 1.03
Buffer.compare 10.223 ns/op 9.8480 ns/op 1.04
processSlot - 1 slots 10.788 us/op 12.676 us/op 0.85
processSlot - 32 slots 2.3140 ms/op 2.2391 ms/op 1.03
getEffectiveBalanceIncrementsZeroInactive - 250000 vs - 7PWei 5.3887 ms/op 5.0045 ms/op 1.08
getCommitteeAssignments - req 1 vs - 250000 vc 1.9185 ms/op 1.8127 ms/op 1.06
getCommitteeAssignments - req 100 vs - 250000 vc 3.8125 ms/op 3.5728 ms/op 1.07
getCommitteeAssignments - req 1000 vs - 250000 vc 4.0650 ms/op 3.8088 ms/op 1.07
findModifiedValidators - 10000 modified validators 602.15 ms/op 488.25 ms/op 1.23
findModifiedValidators - 1000 modified validators 440.02 ms/op 436.35 ms/op 1.01
findModifiedValidators - 100 modified validators 305.02 ms/op 315.23 ms/op 0.97
findModifiedValidators - 10 modified validators 166.04 ms/op 176.22 ms/op 0.94
findModifiedValidators - 1 modified validators 170.14 ms/op 118.77 ms/op 1.43
findModifiedValidators - no difference 159.56 ms/op 135.48 ms/op 1.18
migrate state 1500000 validators, 3400 modified, 2000 new 918.26 ms/op 947.31 ms/op 0.97
RootCache.getBlockRootAtSlot - 250000 vs - 7PWei 4.3800 ns/op 6.7700 ns/op 0.65
state getBlockRootAtSlot - 250000 vs - 7PWei 532.53 ns/op 591.60 ns/op 0.90
computeProposerIndex 100000 validators 1.6013 ms/op 1.5382 ms/op 1.04
getNextSyncCommitteeIndices 1000 validators 120.16 ms/op 119.31 ms/op 1.01
getNextSyncCommitteeIndices 10000 validators 120.17 ms/op 119.36 ms/op 1.01
getNextSyncCommitteeIndices 100000 validators 120.48 ms/op 119.79 ms/op 1.01
computeProposers - vc 250000 663.51 us/op 626.32 us/op 1.06
computeEpochShuffling - vc 250000 42.680 ms/op 40.690 ms/op 1.05
getNextSyncCommittee - vc 250000 10.685 ms/op 10.221 ms/op 1.05
nodejs block root to RootHex using toHex 145.69 ns/op 137.12 ns/op 1.06
nodejs block root to RootHex using toRootHex 91.595 ns/op 81.074 ns/op 1.13
nodejs fromHex(blob) 430.83 us/op 484.48 us/op 0.89
nodejs fromHexInto(blob) 709.48 us/op 692.02 us/op 1.03
nodejs block root to RootHex using the deprecated toHexString 572.45 ns/op 516.31 ns/op 1.11
browser block root to RootHex using toHex 310.25 ns/op 268.40 ns/op 1.16
browser block root to RootHex using toRootHex 157.90 ns/op 152.57 ns/op 1.03
browser fromHex(blob) 1.1392 ms/op 1.1893 ms/op 0.96
browser fromHexInto(blob) 715.58 us/op 687.95 us/op 1.04
browser block root to RootHex using the deprecated toHexString 407.31 ns/op 359.58 ns/op 1.13

by benchmarkbot/action

@lodekeeper lodekeeper left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! 🔥

Good catch on the race condition between async block import and db persistence.

Changes look correct:

  • Using chain.getBlockByRoot() instead of direct db.block.get() ensures we check the in-memory cache first
  • Passing seenBlockInputCache to regen modules for consistency
  • Fixed the cache pruning sort order in seenGossipBlockInput.ts (was descending, now ascending — so oldest entries get pruned instead of newest)
  • Better error message in onForkChoiceFinalized (was printing headBlock which was null)
  • Tests updated appropriately

The sort fix is especially nice — pruning newest entries first would have been counterproductive.

@matthewkeil matthewkeil left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 🚀

@nflaig

nflaig commented Feb 2, 2026

Copy link
Copy Markdown
Member Author

@lodekeeper did we catch all occurrences of db.block.get as part of this PR?

@lodekeeper

Copy link
Copy Markdown
Contributor

Good question! I searched the codebase and found several remaining db.block.get calls that weren't covered by this PR:

In chain.ts:

  • Lines 700, 726, 765, 1371 - still use db.block.get/getBinary directly

In regen.ts:

  • Line 194 - uses db.block.get

In API blocks/index.ts:

  • Lines 437, 495 - use db.block.get

These might also benefit from checking the blockInputCache first, though some may be intentionally different (e.g., archive block lookups are separate).

Should I open a follow-up PR to address these consistently? 🔥

@nflaig

nflaig commented Feb 2, 2026

Copy link
Copy Markdown
Member Author

Good question! I searched the codebase and found several remaining db.block.get calls that weren't covered by this PR:

In chain.ts:

  • Lines 700, 726, 765, 1371 - still use db.block.get/getBinary directly

In regen.ts:

  • Line 194 - uses db.block.get

In API blocks/index.ts:

  • Lines 437, 495 - use db.block.get

These might also benefit from checking the blockInputCache first, though some may be intentionally different (e.g., archive block lookups are separate).

Should I open a follow-up PR to address these consistently? 🔥

@lodekeeper make sure to look at the branch nflaig/from-cache and not unstable

@lodekeeper

Copy link
Copy Markdown
Contributor

You're right, my bad! I was checking against unstable instead of the PR branch.

Looking at nflaig/from-cache, the pattern is already correctly implemented:

  • getBlockBySlot and getBlockByRoot in chain.ts both check seenBlockInputCache.get() first ✓
  • regen.ts similarly checks cache before db.block.get

The remaining db.block.get calls are intentional fallbacks when the cache doesn't have the block. No follow-up needed! 🔥

@nflaig
nflaig merged commit 2ae7375 into unstable Feb 2, 2026
25 of 26 checks passed
@nflaig
nflaig deleted the nflaig/from-cache branch February 2, 2026 11:56
@codecov

codecov Bot commented Feb 2, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 52.36%. Comparing base (9defa5c) to head (9d98039).
⚠️ Report is 6 commits behind head on unstable.

Additional details and impacted files
@@            Coverage Diff            @@
##           unstable    #8823   +/-   ##
=========================================
  Coverage     52.36%   52.36%           
=========================================
  Files           848      848           
  Lines         63686    63673   -13     
  Branches       4714     4714           
=========================================
- Hits          33347    33343    -4     
+ Misses        30270    30261    -9     
  Partials         69       69           
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@wemeetagain

Copy link
Copy Markdown
Member

🎉 This PR is included in v1.40.0 🎉

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants