Skip to content

feat: prune PayloadEnvelopeInput when syncing#9509

Merged
nflaig merged 2 commits into
unstablefrom
te/prune_below_parent_on_block_import
Jun 30, 2026
Merged

feat: prune PayloadEnvelopeInput when syncing#9509
nflaig merged 2 commits into
unstablefrom
te/prune_below_parent_on_block_import

Conversation

@twoeths

@twoeths twoeths commented Jun 15, 2026

Copy link
Copy Markdown
Member

Motivation

  • We only call pruneBelowParent() when the node is synced, ie in "PrepareNextSlot", this may cause OOM when the node is syncing and unfinality network as in glamsterdam-devnet-5

Description

  • call pruneBelowParent() after importBlock()

this is an alternative to #9489

Created with the help of Claude

@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 refactors the pruning of the gloas payload-envelope cache. The pruning logic is moved from prepareNextSlot.ts to importBlock.ts and scheduled to run in the next event loop. Additionally, pruneBelowParent in seenPayloadEnvelopeInput.ts is updated to only evict when the payload status is FULL and all data has been computed, preventing premature eviction during syncing or column gathering. Corresponding unit tests have been added to verify these new conditions. There are no review comments, and I have no further feedback to provide.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

@github-actions

github-actions Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Performance Report

✔️ no performance regression detected

Full benchmark results
Benchmark suite Current: 4c5a060 Previous: ec43878 Ratio
getPubkeys - index2pubkey - req 1000 vs - 250000 vc 864.76 us/op 1.1251 ms/op 0.77
getPubkeys - validatorsArr - req 1000 vs - 250000 vc 38.455 us/op 38.009 us/op 1.01
BLS verify - blst 695.24 us/op 714.64 us/op 0.97
BLS verifyMultipleSignatures 3 - blst 1.2829 ms/op 1.2957 ms/op 0.99
BLS verifyMultipleSignatures 8 - blst 2.0484 ms/op 2.0482 ms/op 1.00
BLS verifyMultipleSignatures 32 - blst 6.4196 ms/op 6.5241 ms/op 0.98
BLS verifyMultipleSignatures 64 - blst 12.495 ms/op 12.536 ms/op 1.00
BLS verifyMultipleSignatures 128 - blst 24.499 ms/op 24.274 ms/op 1.01
BLS deserializing 10000 signatures 620.66 ms/op 615.04 ms/op 1.01
BLS deserializing 100000 signatures 6.2728 s/op 6.2313 s/op 1.01
BLS verifyMultipleSignatures - same message - 3 - blst 773.37 us/op 760.44 us/op 1.02
BLS verifyMultipleSignatures - same message - 8 - blst 911.65 us/op 797.14 us/op 1.14
BLS verifyMultipleSignatures - same message - 32 - blst 1.4225 ms/op 1.4308 ms/op 0.99
BLS verifyMultipleSignatures - same message - 64 - blst 2.3075 ms/op 2.2266 ms/op 1.04
BLS verifyMultipleSignatures - same message - 128 - blst 3.9840 ms/op 3.8332 ms/op 1.04
BLS aggregatePubkeys 32 - blst 17.500 us/op 16.857 us/op 1.04
BLS aggregatePubkeys 128 - blst 61.502 us/op 60.604 us/op 1.01
getSlashingsAndExits - default max 42.475 us/op 52.937 us/op 0.80
getSlashingsAndExits - 2k 370.91 us/op 401.99 us/op 0.92
proposeBlockBody type=full, size=empty 636.97 us/op 1.0577 ms/op 0.60
isKnown best case - 1 super set check 167.00 ns/op 183.00 ns/op 0.91
isKnown normal case - 2 super set checks 165.00 ns/op 168.00 ns/op 0.98
isKnown worse case - 16 super set checks 155.00 ns/op 168.00 ns/op 0.92
validate api signedAggregateAndProof - struct 1.4602 ms/op 1.4416 ms/op 1.01
validate gossip signedAggregateAndProof - struct 1.4544 ms/op 1.4320 ms/op 1.02
batch validate gossip attestation - vc 640000 - chunk 32 103.42 us/op 103.01 us/op 1.00
batch validate gossip attestation - vc 640000 - chunk 64 92.522 us/op 90.582 us/op 1.02
batch validate gossip attestation - vc 640000 - chunk 128 84.178 us/op 86.425 us/op 0.97
batch validate gossip attestation - vc 640000 - chunk 256 81.546 us/op 82.045 us/op 0.99
bytes32 toHexString 272.00 ns/op 268.00 ns/op 1.01
bytes32 Buffer.toString(hex) 153.00 ns/op 154.00 ns/op 0.99
bytes32 Buffer.toString(hex) from Uint8Array 215.00 ns/op 219.00 ns/op 0.98
bytes32 Buffer.toString(hex) + 0x 159.00 ns/op 157.00 ns/op 1.01
Return object 10000 times 0.21110 ns/op 0.20650 ns/op 1.02
Throw Error 10000 times 3.3518 us/op 3.1878 us/op 1.05
toHex 86.208 ns/op 85.223 ns/op 1.01
Buffer.from 78.353 ns/op 77.166 ns/op 1.02
shared Buffer 51.586 ns/op 50.175 ns/op 1.03
fastMsgIdFn sha256 / 200 bytes 1.4250 us/op 1.4160 us/op 1.01
fastMsgIdFn h32 xxhash / 200 bytes 146.00 ns/op 155.00 ns/op 0.94
fastMsgIdFn h64 xxhash / 200 bytes 196.00 ns/op 196.00 ns/op 1.00
fastMsgIdFn sha256 / 1000 bytes 4.6260 us/op 4.5440 us/op 1.02
fastMsgIdFn h32 xxhash / 1000 bytes 239.00 ns/op 239.00 ns/op 1.00
fastMsgIdFn h64 xxhash / 1000 bytes 243.00 ns/op 243.00 ns/op 1.00
fastMsgIdFn sha256 / 10000 bytes 41.105 us/op 40.260 us/op 1.02
fastMsgIdFn h32 xxhash / 10000 bytes 1.2840 us/op 1.2430 us/op 1.03
fastMsgIdFn h64 xxhash / 10000 bytes 846.00 ns/op 801.00 ns/op 1.06
send data - 1000 256B messages 3.9377 ms/op 4.1869 ms/op 0.94
send data - 1000 512B messages 5.4290 ms/op 5.1000 ms/op 1.06
send data - 1000 1024B messages 5.6565 ms/op 5.3450 ms/op 1.06
send data - 1000 1200B messages 5.4609 ms/op 5.8489 ms/op 0.93
send data - 1000 2048B messages 14.494 ms/op 13.336 ms/op 1.09
send data - 1000 4096B messages 27.512 ms/op 62.123 ms/op 0.44
send data - 1000 16384B messages 137.06 ms/op 262.19 ms/op 0.52
send data - 1000 65536B messages 478.67 ms/op 981.41 ms/op 0.49
enrSubnets - fastDeserialize 64 bits 693.00 ns/op 725.00 ns/op 0.96
enrSubnets - ssz BitVector 64 bits 254.00 ns/op 263.00 ns/op 0.97
enrSubnets - fastDeserialize 4 bits 97.000 ns/op 98.000 ns/op 0.99
enrSubnets - ssz BitVector 4 bits 257.00 ns/op 271.00 ns/op 0.95
prioritizePeers score -10:0 att 32-0.1 sync 2-0 198.26 us/op 199.26 us/op 0.99
prioritizePeers score 0:0 att 32-0.25 sync 2-0.25 225.52 us/op 241.86 us/op 0.93
prioritizePeers score 0:0 att 32-0.5 sync 2-0.5 329.21 us/op 339.23 us/op 0.97
prioritizePeers score 0:0 att 64-0.75 sync 4-0.75 584.55 us/op 585.72 us/op 1.00
prioritizePeers score 0:0 att 64-1 sync 4-1 681.21 us/op 702.01 us/op 0.97
array of 16000 items push then shift 1.2352 us/op 1.2514 us/op 0.99
LinkedList of 16000 items push then shift 7.3630 ns/op 7.4490 ns/op 0.99
array of 16000 items push then pop 64.847 ns/op 66.852 ns/op 0.97
LinkedList of 16000 items push then pop 5.8990 ns/op 5.8890 ns/op 1.00
array of 24000 items push then shift 1.8148 us/op 1.8326 us/op 0.99
LinkedList of 24000 items push then shift 7.0520 ns/op 6.9370 ns/op 1.02
array of 24000 items push then pop 91.157 ns/op 92.712 ns/op 0.98
LinkedList of 24000 items push then pop 5.8890 ns/op 5.9340 ns/op 0.99
intersect bitArray bitLen 8 4.6410 ns/op 4.7100 ns/op 0.99
intersect array and set length 8 28.557 ns/op 28.811 ns/op 0.99
intersect bitArray bitLen 128 23.523 ns/op 23.378 ns/op 1.01
intersect array and set length 128 486.84 ns/op 481.33 ns/op 1.01
bitArray.getTrueBitIndexes() bitLen 128 935.00 ns/op 1.0190 us/op 0.92
bitArray.getTrueBitIndexes() bitLen 248 1.6840 us/op 1.7700 us/op 0.95
bitArray.getTrueBitIndexes() bitLen 512 3.4860 us/op 3.5520 us/op 0.98
Full columns - reconstruct all 6 blobs 172.26 us/op 164.54 us/op 1.05
Full columns - reconstruct half of the blobs out of 6 112.76 us/op 83.044 us/op 1.36
Full columns - reconstruct single blob out of 6 40.991 us/op 30.963 us/op 1.32
Half columns - reconstruct all 6 blobs 381.03 ms/op 372.37 ms/op 1.02
Half columns - reconstruct half of the blobs out of 6 190.29 ms/op 189.65 ms/op 1.00
Half columns - reconstruct single blob out of 6 67.303 ms/op 67.556 ms/op 1.00
Full columns - reconstruct all 10 blobs 466.19 us/op 282.88 us/op 1.65
Full columns - reconstruct half of the blobs out of 10 208.10 us/op 106.31 us/op 1.96
Full columns - reconstruct single blob out of 10 43.059 us/op 88.580 us/op 0.49
Half columns - reconstruct all 10 blobs 620.93 ms/op 639.69 ms/op 0.97
Half columns - reconstruct half of the blobs out of 10 317.59 ms/op 316.52 ms/op 1.00
Half columns - reconstruct single blob out of 10 68.208 ms/op 68.420 ms/op 1.00
Full columns - reconstruct all 20 blobs 821.10 us/op 522.50 us/op 1.57
Full columns - reconstruct half of the blobs out of 20 507.42 us/op 179.68 us/op 2.82
Full columns - reconstruct single blob out of 20 54.756 us/op 35.722 us/op 1.53
Half columns - reconstruct all 20 blobs 1.2650 s/op 1.2399 s/op 1.02
Half columns - reconstruct half of the blobs out of 20 626.50 ms/op 619.90 ms/op 1.01
Half columns - reconstruct single blob out of 20 67.180 ms/op 67.772 ms/op 0.99
Set add up to 64 items then delete first 2.4069 us/op 2.0488 us/op 1.17
OrderedSet add up to 64 items then delete first 3.1982 us/op 3.2760 us/op 0.98
Set add up to 64 items then delete last 2.2521 us/op 2.0362 us/op 1.11
OrderedSet add up to 64 items then delete last 3.1555 us/op 3.2666 us/op 0.97
Set add up to 64 items then delete middle 2.0447 us/op 2.0614 us/op 0.99
OrderedSet add up to 64 items then delete middle 4.6068 us/op 4.7077 us/op 0.98
Set add up to 128 items then delete first 4.9631 us/op 3.9456 us/op 1.26
OrderedSet add up to 128 items then delete first 6.7172 us/op 5.8210 us/op 1.15
Set add up to 128 items then delete last 3.7084 us/op 3.7374 us/op 0.99
OrderedSet add up to 128 items then delete last 5.7049 us/op 5.7836 us/op 0.99
Set add up to 128 items then delete middle 3.6851 us/op 3.7719 us/op 0.98
OrderedSet add up to 128 items then delete middle 11.608 us/op 11.788 us/op 0.98
Set add up to 256 items then delete first 10.501 us/op 7.4857 us/op 1.40
OrderedSet add up to 256 items then delete first 12.522 us/op 11.263 us/op 1.11
Set add up to 256 items then delete last 7.5165 us/op 7.5387 us/op 1.00
OrderedSet add up to 256 items then delete last 11.735 us/op 11.707 us/op 1.00
Set add up to 256 items then delete middle 7.3084 us/op 7.4261 us/op 0.98
OrderedSet add up to 256 items then delete middle 35.874 us/op 33.770 us/op 1.06
runFastConfirmationRules vc:100000 bc:96 eq:0 2.1690 us/op 2.0660 us/op 1.05
runFastConfirmationRules vc:600000 bc:96 eq:0 2.2710 us/op 2.1290 us/op 1.07
runFastConfirmationRules vc:1000000 bc:96 eq:0 2.0650 us/op 1.9730 us/op 1.05
runFastConfirmationRules vc:600000 bc:320 eq:0 4.3120 us/op 4.7770 us/op 0.90
runFastConfirmationRules vc:600000 bc:1200 eq:0 17.520 us/op 17.072 us/op 1.03
runFastConfirmationRules vc:600000 bc:96 eq:1000 2.0530 us/op 1.8430 us/op 1.11
runFastConfirmationRules vc:600000 bc:96 eq:10000 2.0180 us/op 1.8800 us/op 1.07
runFastConfirmationRules vc:600000 bc:96 eq:300000 2.0880 us/op 2.1030 us/op 0.99
pass gossip attestations to forkchoice per slot 2.5240 ms/op 2.4173 ms/op 1.04
forkChoice updateHead vc 100000 bc 64 eq 0 400.56 us/op 386.75 us/op 1.04
forkChoice updateHead vc 600000 bc 64 eq 0 2.3593 ms/op 2.2992 ms/op 1.03
forkChoice updateHead vc 1000000 bc 64 eq 0 3.9444 ms/op 3.8148 ms/op 1.03
forkChoice updateHead vc 600000 bc 320 eq 0 2.3908 ms/op 2.3186 ms/op 1.03
forkChoice updateHead vc 600000 bc 1200 eq 0 2.4884 ms/op 2.3213 ms/op 1.07
forkChoice updateHead vc 600000 bc 7200 eq 0 2.7638 ms/op 2.6206 ms/op 1.05
forkChoice updateHead vc 600000 bc 64 eq 1000 2.4126 ms/op 2.3303 ms/op 1.04
forkChoice updateHead vc 600000 bc 64 eq 10000 2.4563 ms/op 2.4020 ms/op 1.02
forkChoice updateHead vc 600000 bc 64 eq 300000 6.8786 ms/op 6.4717 ms/op 1.06
computeDeltas 1400000 validators 0% inactive 12.340 ms/op 11.879 ms/op 1.04
computeDeltas 1400000 validators 10% inactive 11.558 ms/op 11.113 ms/op 1.04
computeDeltas 1400000 validators 20% inactive 10.379 ms/op 10.132 ms/op 1.02
computeDeltas 1400000 validators 50% inactive 8.1819 ms/op 7.8060 ms/op 1.05
computeDeltas 2100000 validators 0% inactive 18.679 ms/op 17.767 ms/op 1.05
computeDeltas 2100000 validators 10% inactive 17.455 ms/op 16.650 ms/op 1.05
computeDeltas 2100000 validators 20% inactive 15.965 ms/op 15.230 ms/op 1.05
computeDeltas 2100000 validators 50% inactive 9.3644 ms/op 8.8329 ms/op 1.06
altair processAttestation - 250000 vs - 7PWei normalcase 2.2057 ms/op 2.4132 ms/op 0.91
altair processAttestation - 250000 vs - 7PWei worstcase 2.6061 ms/op 2.5567 ms/op 1.02
altair processAttestation - setStatus - 1/6 committees join 104.37 us/op 91.285 us/op 1.14
altair processAttestation - setStatus - 1/3 committees join 199.94 us/op 187.17 us/op 1.07
altair processAttestation - setStatus - 1/2 committees join 280.48 us/op 260.88 us/op 1.08
altair processAttestation - setStatus - 2/3 committees join 357.25 us/op 336.59 us/op 1.06
altair processAttestation - setStatus - 4/5 committees join 495.32 us/op 477.11 us/op 1.04
altair processAttestation - setStatus - 100% committees join 600.41 us/op 569.14 us/op 1.05
altair processBlock - 250000 vs - 7PWei normalcase 3.8589 ms/op 3.9915 ms/op 0.97
altair processBlock - 250000 vs - 7PWei normalcase hashState 17.174 ms/op 17.008 ms/op 1.01
altair processBlock - 250000 vs - 7PWei worstcase 21.395 ms/op 20.172 ms/op 1.06
altair processBlock - 250000 vs - 7PWei worstcase hashState 41.297 ms/op 38.099 ms/op 1.08
phase0 processBlock - 250000 vs - 7PWei normalcase 1.4690 ms/op 1.3425 ms/op 1.09
phase0 processBlock - 250000 vs - 7PWei worstcase 17.985 ms/op 18.124 ms/op 0.99
altair processEth1Data - 250000 vs - 7PWei normalcase 288.01 us/op 273.38 us/op 1.05
getExpectedWithdrawals 250000 eb:1,eth1:1,we:0,wn:0,smpl:16 3.2860 us/op 4.7590 us/op 0.69
getExpectedWithdrawals 250000 eb:0.95,eth1:0.1,we:0.05,wn:0,smpl:220 24.129 us/op 20.046 us/op 1.20
getExpectedWithdrawals 250000 eb:0.95,eth1:0.3,we:0.05,wn:0,smpl:43 5.8170 us/op 5.3040 us/op 1.10
getExpectedWithdrawals 250000 eb:0.95,eth1:0.7,we:0.05,wn:0,smpl:19 3.5970 us/op 3.4430 us/op 1.04
getExpectedWithdrawals 250000 eb:0.1,eth1:0.1,we:0,wn:0,smpl:1021 89.305 us/op 84.447 us/op 1.06
getExpectedWithdrawals 250000 eb:0.03,eth1:0.03,we:0,wn:0,smpl:11778 1.3820 ms/op 1.3559 ms/op 1.02
getExpectedWithdrawals 250000 eb:0.01,eth1:0.01,we:0,wn:0,smpl:16384 1.8112 ms/op 1.7406 ms/op 1.04
getExpectedWithdrawals 250000 eb:0,eth1:0,we:0,wn:0,smpl:16384 1.8086 ms/op 1.7599 ms/op 1.03
getExpectedWithdrawals 250000 eb:0,eth1:0,we:0,wn:0,nocache,smpl:16384 3.6238 ms/op 3.6330 ms/op 1.00
getExpectedWithdrawals 250000 eb:0,eth1:1,we:0,wn:0,smpl:16384 2.0391 ms/op 1.9883 ms/op 1.03
getExpectedWithdrawals 250000 eb:0,eth1:1,we:0,wn:0,nocache,smpl:16384 3.9269 ms/op 5.7404 ms/op 0.68
Tree 40 250000 create 369.82 ms/op 340.26 ms/op 1.09
Tree 40 250000 get(125000) 92.125 ns/op 90.708 ns/op 1.02
Tree 40 250000 set(125000) 1.0326 us/op 998.75 ns/op 1.03
Tree 40 250000 toArray() 12.202 ms/op 14.058 ms/op 0.87
Tree 40 250000 iterate all - toArray() + loop 13.573 ms/op 17.067 ms/op 0.80
Tree 40 250000 iterate all - get(i) 37.434 ms/op 39.354 ms/op 0.95
Array 250000 create 2.3702 ms/op 2.1683 ms/op 1.09
Array 250000 clone - spread 735.12 us/op 665.22 us/op 1.11
Array 250000 get(125000) 0.29300 ns/op 0.28700 ns/op 1.02
Array 250000 set(125000) 0.29400 ns/op 0.28700 ns/op 1.02
Array 250000 iterate all - loop 57.626 us/op 55.581 us/op 1.04
phase0 afterProcessEpoch - 250000 vs - 7PWei 40.571 ms/op 39.889 ms/op 1.02
Array.fill - length 1000000 2.1167 ms/op 2.0528 ms/op 1.03
Array push - length 1000000 10.074 ms/op 8.7227 ms/op 1.15
Array.get 0.20766 ns/op 0.20042 ns/op 1.04
Uint8Array.get 0.23876 ns/op 0.22607 ns/op 1.06
phase0 beforeProcessEpoch - 250000 vs - 7PWei 16.699 ms/op 16.782 ms/op 1.00
altair processEpoch - mainnet_e81889 253.27 ms/op 253.02 ms/op 1.00
mainnet_e81889 - altair beforeProcessEpoch 14.672 ms/op 13.662 ms/op 1.07
mainnet_e81889 - altair processJustificationAndFinalization 5.6690 us/op 5.5160 us/op 1.03
mainnet_e81889 - altair processInactivityUpdates 3.3840 ms/op 3.2894 ms/op 1.03
mainnet_e81889 - altair processRewardsAndPenalties 19.264 ms/op 18.686 ms/op 1.03
mainnet_e81889 - altair processRegistryUpdates 532.00 ns/op 523.00 ns/op 1.02
mainnet_e81889 - altair processSlashings 134.00 ns/op 123.00 ns/op 1.09
mainnet_e81889 - altair processEth1DataReset 124.00 ns/op 122.00 ns/op 1.02
mainnet_e81889 - altair processEffectiveBalanceUpdates 1.4454 ms/op 2.6216 ms/op 0.55
mainnet_e81889 - altair processSlashingsReset 684.00 ns/op 670.00 ns/op 1.02
mainnet_e81889 - altair processRandaoMixesReset 1.1500 us/op 1.0850 us/op 1.06
mainnet_e81889 - altair processHistoricalRootsUpdate 126.00 ns/op 124.00 ns/op 1.02
mainnet_e81889 - altair processParticipationFlagUpdates 414.00 ns/op 425.00 ns/op 0.97
mainnet_e81889 - altair processSyncCommitteeUpdates 99.000 ns/op 101.00 ns/op 0.98
mainnet_e81889 - altair afterProcessEpoch 40.993 ms/op 39.839 ms/op 1.03
capella processEpoch - mainnet_e217614 806.61 ms/op 812.38 ms/op 0.99
mainnet_e217614 - capella beforeProcessEpoch 64.683 ms/op 59.118 ms/op 1.09
mainnet_e217614 - capella processJustificationAndFinalization 6.3000 us/op 6.6170 us/op 0.95
mainnet_e217614 - capella processInactivityUpdates 14.894 ms/op 15.011 ms/op 0.99
mainnet_e217614 - capella processRewardsAndPenalties 90.049 ms/op 87.563 ms/op 1.03
mainnet_e217614 - capella processRegistryUpdates 4.5350 us/op 4.4270 us/op 1.02
mainnet_e217614 - capella processSlashings 135.00 ns/op 131.00 ns/op 1.03
mainnet_e217614 - capella processEth1DataReset 125.00 ns/op 126.00 ns/op 0.99
mainnet_e217614 - capella processEffectiveBalanceUpdates 16.468 ms/op 16.389 ms/op 1.00
mainnet_e217614 - capella processSlashingsReset 672.00 ns/op 687.00 ns/op 0.98
mainnet_e217614 - capella processRandaoMixesReset 1.2080 us/op 1.3250 us/op 0.91
mainnet_e217614 - capella processHistoricalRootsUpdate 128.00 ns/op 125.00 ns/op 1.02
mainnet_e217614 - capella processParticipationFlagUpdates 426.00 ns/op 432.00 ns/op 0.99
mainnet_e217614 - capella afterProcessEpoch 107.09 ms/op 106.06 ms/op 1.01
phase0 processEpoch - mainnet_e58758 292.32 ms/op 303.35 ms/op 0.96
mainnet_e58758 - phase0 beforeProcessEpoch 64.252 ms/op 65.854 ms/op 0.98
mainnet_e58758 - phase0 processJustificationAndFinalization 6.0440 us/op 6.4330 us/op 0.94
mainnet_e58758 - phase0 processRewardsAndPenalties 15.910 ms/op 15.579 ms/op 1.02
mainnet_e58758 - phase0 processRegistryUpdates 2.2590 us/op 2.2270 us/op 1.01
mainnet_e58758 - phase0 processSlashings 236.00 ns/op 126.00 ns/op 1.87
mainnet_e58758 - phase0 processEth1DataReset 131.00 ns/op 125.00 ns/op 1.05
mainnet_e58758 - phase0 processEffectiveBalanceUpdates 796.56 us/op 804.01 us/op 0.99
mainnet_e58758 - phase0 processSlashingsReset 883.00 ns/op 886.00 ns/op 1.00
mainnet_e58758 - phase0 processRandaoMixesReset 1.2460 us/op 1.2860 us/op 0.97
mainnet_e58758 - phase0 processHistoricalRootsUpdate 128.00 ns/op 128.00 ns/op 1.00
mainnet_e58758 - phase0 processParticipationRecordUpdates 1.1780 us/op 1.2150 us/op 0.97
mainnet_e58758 - phase0 afterProcessEpoch 33.346 ms/op 33.081 ms/op 1.01
phase0 processEffectiveBalanceUpdates - 250000 normalcase 970.14 us/op 1.0000 ms/op 0.97
phase0 processEffectiveBalanceUpdates - 250000 worstcase 0.5 1.5021 ms/op 1.5139 ms/op 0.99
altair processInactivityUpdates - 250000 normalcase 11.257 ms/op 11.330 ms/op 0.99
altair processInactivityUpdates - 250000 worstcase 11.113 ms/op 10.528 ms/op 1.06
phase0 processRegistryUpdates - 250000 normalcase 2.0770 us/op 2.1960 us/op 0.95
phase0 processRegistryUpdates - 250000 badcase_full_deposits 143.13 us/op 141.09 us/op 1.01
phase0 processRegistryUpdates - 250000 worstcase 0.5 66.610 ms/op 65.151 ms/op 1.02
altair processRewardsAndPenalties - 250000 normalcase 15.506 ms/op 15.687 ms/op 0.99
altair processRewardsAndPenalties - 250000 worstcase 14.952 ms/op 15.451 ms/op 0.97
phase0 getAttestationDeltas - 250000 normalcase 5.2075 ms/op 5.1360 ms/op 1.01
phase0 getAttestationDeltas - 250000 worstcase 5.2182 ms/op 5.2624 ms/op 0.99
phase0 processSlashings - 250000 worstcase 59.218 us/op 58.627 us/op 1.01
altair processSyncCommitteeUpdates - 250000 10.278 ms/op 10.495 ms/op 0.98
BeaconState.hashTreeRoot - No change 164.00 ns/op 160.00 ns/op 1.02
BeaconState.hashTreeRoot - 1 full validator 66.760 us/op 71.734 us/op 0.93
BeaconState.hashTreeRoot - 32 full validator 820.11 us/op 799.54 us/op 1.03
BeaconState.hashTreeRoot - 512 full validator 6.5624 ms/op 6.8472 ms/op 0.96
BeaconState.hashTreeRoot - 1 validator.effectiveBalance 92.515 us/op 85.474 us/op 1.08
BeaconState.hashTreeRoot - 32 validator.effectiveBalance 1.3014 ms/op 1.4534 ms/op 0.90
BeaconState.hashTreeRoot - 512 validator.effectiveBalance 15.314 ms/op 15.887 ms/op 0.96
BeaconState.hashTreeRoot - 1 balances 69.423 us/op 69.520 us/op 1.00
BeaconState.hashTreeRoot - 32 balances 709.91 us/op 694.91 us/op 1.02
BeaconState.hashTreeRoot - 512 balances 5.2708 ms/op 5.5884 ms/op 0.94
BeaconState.hashTreeRoot - 250000 balances 123.43 ms/op 118.01 ms/op 1.05
aggregationBits - 2048 els - zipIndexesInBitList 19.440 us/op 18.883 us/op 1.03
regular array get 100000 times 22.548 us/op 22.456 us/op 1.00
wrappedArray get 100000 times 22.547 us/op 22.500 us/op 1.00
arrayWithProxy get 100000 times 9.5163 ms/op 17.597 ms/op 0.54
ssz.Root.equals 20.981 ns/op 20.914 ns/op 1.00
byteArrayEquals 20.775 ns/op 20.700 ns/op 1.00
Buffer.compare 8.5790 ns/op 8.5810 ns/op 1.00
processSlot - 1 slots 8.5630 us/op 8.1800 us/op 1.05
processSlot - 32 slots 1.9696 ms/op 2.0721 ms/op 0.95
getEffectiveBalanceIncrementsZeroInactive - 250000 vs - 7PWei 4.3081 ms/op 3.9994 ms/op 1.08
getCommitteeAssignments - req 1 vs - 250000 vc 1.6067 ms/op 1.6062 ms/op 1.00
getCommitteeAssignments - req 100 vs - 250000 vc 3.3229 ms/op 3.2735 ms/op 1.02
getCommitteeAssignments - req 1000 vs - 250000 vc 3.5850 ms/op 3.5243 ms/op 1.02
findModifiedValidators - 10000 modified validators 713.84 ms/op 730.03 ms/op 0.98
findModifiedValidators - 1000 modified validators 462.78 ms/op 588.25 ms/op 0.79
findModifiedValidators - 100 modified validators 302.29 ms/op 295.77 ms/op 1.02
findModifiedValidators - 10 modified validators 214.58 ms/op 230.03 ms/op 0.93
findModifiedValidators - 1 modified validators 209.32 ms/op 176.67 ms/op 1.18
findModifiedValidators - no difference 142.36 ms/op 169.46 ms/op 0.84
migrate state 1500000 validators, 3400 modified, 2000 new 3.0766 s/op 2.9727 s/op 1.03
RootCache.getBlockRootAtSlot - 250000 vs - 7PWei 3.6000 ns/op 3.5600 ns/op 1.01
state getBlockRootAtSlot - 250000 vs - 7PWei 417.57 ns/op 345.95 ns/op 1.21
computeProposerIndex 100000 validators 1.4431 ms/op 1.3199 ms/op 1.09
getNextSyncCommitteeIndices 1000 validators 3.8299 ms/op 2.8043 ms/op 1.37
getNextSyncCommitteeIndices 10000 validators 34.369 ms/op 24.922 ms/op 1.38
getNextSyncCommitteeIndices 100000 validators 113.76 ms/op 85.439 ms/op 1.33
computeProposers - vc 250000 554.05 us/op 536.45 us/op 1.03
computeEpochShuffling - vc 250000 38.810 ms/op 39.412 ms/op 0.98
getNextSyncCommittee - vc 250000 9.7795 ms/op 9.4043 ms/op 1.04
nodejs block root to RootHex using toHex 84.930 ns/op 88.065 ns/op 0.96
nodejs block root to RootHex using toRootHex 50.890 ns/op 56.373 ns/op 0.90
nodejs fromHex(blob) 741.99 us/op 772.62 us/op 0.96
nodejs fromHexInto(blob) 608.65 us/op 604.11 us/op 1.01
nodejs block root to RootHex using the deprecated toHexString 469.32 ns/op 484.02 ns/op 0.97
nodejs byteArrayEquals 32 bytes (block root) 25.121 ns/op 25.265 ns/op 0.99
nodejs byteArrayEquals 48 bytes (pubkey) 36.516 ns/op 38.305 ns/op 0.95
nodejs byteArrayEquals 96 bytes (signature) 34.669 ns/op 33.963 ns/op 1.02
nodejs byteArrayEquals 1024 bytes 41.570 ns/op 41.554 ns/op 1.00
nodejs byteArrayEquals 131072 bytes (blob) 1.7225 us/op 1.7408 us/op 0.99
browser block root to RootHex using toHex 140.03 ns/op 141.16 ns/op 0.99
browser block root to RootHex using toRootHex 127.09 ns/op 128.61 ns/op 0.99
browser fromHex(blob) 1.4655 ms/op 1.4854 ms/op 0.99
browser fromHexInto(blob) 623.10 us/op 612.88 us/op 1.02
browser block root to RootHex using the deprecated toHexString 477.23 ns/op 327.61 ns/op 1.46
browser byteArrayEquals 32 bytes (block root) 27.410 ns/op 27.025 ns/op 1.01
browser byteArrayEquals 48 bytes (pubkey) 39.053 ns/op 38.512 ns/op 1.01
browser byteArrayEquals 96 bytes (signature) 73.153 ns/op 71.956 ns/op 1.02
browser byteArrayEquals 1024 bytes 736.74 ns/op 727.30 ns/op 1.01
browser byteArrayEquals 131072 bytes (blob) 93.320 us/op 92.560 us/op 1.01

by benchmarkbot/action

@twoeths twoeths marked this pull request as ready for review June 15, 2026 09:51
@twoeths twoeths requested a review from a team as a code owner June 15, 2026 09:51
@twoeths

twoeths commented Jun 15, 2026

Copy link
Copy Markdown
Member Author

@lodekeeper please review given the context you have here #9489 (comment)

@codecov

codecov Bot commented Jun 15, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 52.58%. Comparing base (4e3a2c0) to head (4a2cfa0).

Additional details and impacted files
@@            Coverage Diff            @@
##           unstable    #9509   +/-   ##
=========================================
  Coverage     52.58%   52.58%           
=========================================
  Files           848      848           
  Lines         60875    60875           
  Branches       4483     4483           
=========================================
  Hits          32013    32013           
  Misses        28800    28800           
  Partials         62       62           
🚀 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.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4a2cfa088c

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

callInNextEventLoop(() => {
const newHeadParent = this.forkChoice.getBlockHexDefaultStatus(newHead.parentRoot);
if (newHeadParent) {
this.seenPayloadEnvelopeInputCache.pruneBelowParent(newHeadParent);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Avoid walking all ancestors on every import

When syncing through a long unfinalized Gloas range, this new per-import prune runs once for every imported block. pruneBelowParent() calls getAllAncestorBlocks(...), which walks from the parent back toward the finalized boundary, so importing N unfinalized blocks does O(N²) ancestor scans in the same non-finality syncing case this change targets and can stall sync on CPU. Please throttle this or track the last pruned point so each import only scans the newly eligible portion.

Useful? React with 👍 / 👎.

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.

Valid concern, and correct in direction. During non-finality the finalized boundary is frozen, so getAllAncestorBlocks walks parent→finalized with depth growing 1→N across imports → Σk = O(N²) node visits + a fresh ProtoNode[] allocation each import. In the empty-branch case this PR targets, those ancestors fail the FULL && hasComputedAllData() guard, so the walk evicts nothing — it's pure re-scan overhead over the same prefix.

Magnitude: negligible for a few epochs (N≈100–300 → tens of thousands of cheap ops). It only bites under prolonged non-finality (thousands of unfinalized blocks) — which is exactly the scenario class this change targets, so worth bounding eventually.

Concrete bounded fix: swap getAllAncestorBlocks for the lazy iterateAncestorBlocks generator and keep a lastPrunedSlot cursor, breaking early once the walk reaches already-pruned depth → aggregate O(N).

One correctness caveat with a pure cursor: an ancestor that was EMPTY when first walked but turns FULL later (payload revealed mid-sync) sits below the cursor and would never be re-visited by pruneBelowParent. pruneFinalized is the backstop (it evicts everything below the finalized slot on the next finalization), so the retained set stays bounded by the non-finality window even with the cursor. If you want precise eviction without leaning on finalization, an evict-on-FULL-transition hook is the alternative, but that's a larger change.

Non-blocking from my side for the immediate devnet fix — fine as a follow-up. Your call on whether to fold it in here.

@lodekeeper

Copy link
Copy Markdown
Contributor

@twoeths reviewed — this is the right direction for the devnet regression, and I'd take it over #9489.

Why it's correct: moving the prune out of prepareNextSlot (which is gated on being near-head/synced, so it effectively doesn't run during deep range sync — that's why the cache grew unbounded mid-sync) into importBlock per-block, after recomputeForkChoiceHead, means eviction is driven by actual fork-choice progression. The pruneBelowParent guard (slot < parent.slot && payloadStatus === FULL && input.hasComputedAllData()) only evicts inputs that are genuinely done, so it does not reintroduce the #9489 "Missing PayloadEnvelopeInput for known block" failure where insert-time pruneToMaxSize dropped entries before their envelopes/columns were consumed.

Test coverage looks good: positive eviction (FULL + computed below parent → evicted), plus the two retention cases (not-yet-FULL, not-computed-all-data) and the parent-slot edge.

One non-blocking perf follow-up: the Codex P2 about the O(N²) ancestor walk is valid in direction for prolonged non-finality — details + a concrete bounded fix in the inline thread. I'd treat it as a follow-up, not a merge blocker for the devnet fix.

Scope caveat (carry-over from #9489): #9509 bounds the cache via import-driven pruning, so it does not cover the original #9073 shape where the cache grows while no blocks import / no fork-choice progression. That OOM path should stay a separate follow-up rather than going back to unsafe insert-time eviction.

@ensi321

ensi321 commented Jun 17, 2026

Copy link
Copy Markdown
Member

it does not cover the original #9073 shape where the cache grows while no blocks import / no fork-choice progression. That OOM path should stay a separate follow-up rather than going back to unsafe insert-time eviction.

I think this is a valid concern

@lodekeeper

Copy link
Copy Markdown
Contributor

Agreed. To pin down where it's tracked: that shape lives in #9073, which stays open after this lands — #9509 references #9489 but doesn't close #9073.

Not a blocker or a reason to revert #9509, though:

So: merge #9509 for the devnet regression, keep #9073 open for the no-progression follow-up.

@wemeetagain

Copy link
Copy Markdown
Member

Agreed. Looked into this briefly with @matthewkeil yesterday. We concluded that this PR is good to patch the immediate issue but does not fix all cases (the obvious one being non-finality with a long-running consistently-minority fork, another being long, long backwards syncing case).

And that directionally, this one seems better than #9489 which is a bit of a blunt instrument with nasty behavior in the backwards syncing case slightly longer than the cache size.

@lodekeeper

Copy link
Copy Markdown
Contributor

Thanks @wemeetagain (and @matthewkeil) — matches the read here. #9509 is the right immediate patch (import-driven eviction, no insert-time data loss), and #9489's insert-time cap is the blunt instrument with the nasty behavior in the backwards-sync-longer-than-cache case.

I've captured your two cases — non-finality with a long-running minority fork, and long backwards/backfill sync — as follow-up in #9073 (the tracking issue for the envelope cache's missing size-cap / sync pruning). Both point at the same fix: a pruneToMaxSize() upper bound independent of fork-choice progression. Not a blocker for merging #9509.

@nflaig

nflaig commented Jun 30, 2026

Copy link
Copy Markdown
Member

@lodekeeper is there anything left to get this merged? if not please approve

@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 — approving. Verified end-to-end:

  • CI all green; mergeable, only the review was outstanding.
  • The fix is sound: pruning moves from prepareNextSlot (which doesn't run during sync, so the cache grew unbounded) into importBlock so it runs on every import, and the new payloadStatus === FULL && hasComputedAllData() guard in pruneBelowParent prevents evicting EMPTY/PENDING envelopes still needed for payload-by-root sync (#9475).
  • The only open review thread (Codex's P2 on the per-import ancestor walk) is a non-blocking follow-up — the walk is bounded by the non-finality window and pruneFinalized is the backstop; the lazy-iterator + cursor optimization can land later.
  • The broader #9073 shape (cache growth while no blocks are imported) stays tracked separately, per the thread with @ensi321 / @wemeetagain.

Nice fix @twoeths.

@lodekeeper

Copy link
Copy Markdown
Contributor

Approved 👍 — but one heads-up: it still shows REVIEW_REQUIRED / BLOCKED after my approval. lodekeeper isn't a write-access collaborator, and GitHub only counts approvals from write-access reviewers toward required-review branch protection — so my approval doesn't satisfy the gate. It'll need an approval from a maintainer/CODEOWNER to actually unblock the merge.

Nothing else is left, though: CI's all green, the Codex P2 (per-import ancestor walk) is a non-blocking follow-up (bounded by the non-finality window, pruneFinalized backstops it), and the broader #9073 no-import-growth shape is tracked separately. So just an eligible approval and it's good to go.

@nflaig nflaig merged commit 762dd5f into unstable Jun 30, 2026
19 checks passed
@nflaig nflaig deleted the te/prune_below_parent_on_block_import branch June 30, 2026 16:41
@wemeetagain

Copy link
Copy Markdown
Member

🎉 This PR is included in v1.44.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.

5 participants