-
Notifications
You must be signed in to change notification settings - Fork 346
fix: sync large blocks without OOMing #2636
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
+675
−198
Merged
Changes from 26 commits
Commits
Show all changes
43 commits
Select commit
Hold shift + click to select a range
be37456
feat:add traces to blocksync
mcrakhman baa5a51
Merge branch 'main' into mcrakhman/block-sync-traces
mcrakhman fa769df
feat: implement changes to blocksync
mcrakhman 9f0ece9
fix: peer eviction after timer reset
mcrakhman 9a84413
debug: add traces for block requests
mcrakhman 606d402
debug: increase peer diversity
mcrakhman 590e6ad
feat: alternate peers
mcrakhman dc8f92e
feat: change blocksync logic
mcrakhman 48e74e3
debug: remove debug prints
mcrakhman bbfa9dc
debug: add correct peer logging
mcrakhman fefcd76
Merge branch 'main' into mcrakhman/block-sync-traces
mcrakhman d9fb221
debug: check previous pool settings
mcrakhman be7f3d7
feat: dynamic pool size
mcrakhman ae326c2
debug: remove info logs on pool size changes
mcrakhman 836d73a
revert: remove info logs on pool size changes
mcrakhman e53ac79
debug: change block limits for larger blocks
mcrakhman 126ca04
debug: add number of requesters to log
mcrakhman 97a2bff
feat: reduce requesters when blocks are large
mcrakhman 08c841d
refactor: pool params calculation
mcrakhman 080d21c
feat: use max block size instead of average
mcrakhman fe4291a
fix: dropped requesters
mcrakhman 6cd7943
debug: log for dropped requester
mcrakhman b05d5a3
refactor: more tests and comments
mcrakhman f51d2f1
refactor: num pending
mcrakhman 8b0abc0
refactor: simplify active peers
mcrakhman a9d117a
feat: add more traces for validate and save time to blocksync
mcrakhman 64b126f
fix: lint and some review fixes
mcrakhman 30576f0
fix: ignore peer id if the request failed
mcrakhman 6e328ef
refactor: renames and change debug levels to trace
mcrakhman 5be511c
fix: review comments
mcrakhman e78d705
fix: race in test
mcrakhman 2f15854
lint: remove unused function
mcrakhman f12bbd7
Merge branch 'main' into mcrakhman/block-sync-traces
mcrakhman 02f924d
feat: don't drop requesters until mem limit is reached
mcrakhman 8401cf8
debug: limit requesters to some fixed value
mcrakhman 9c8ca2a
debug: limit max pending per peer - hard cap
mcrakhman 72d7cfb
refactor: simplify params
mcrakhman 567e3d4
refactor: fix and simplify
mcrakhman 0ea933c
refactor: add more comments
mcrakhman f31a42c
Merge branch 'mcrakhman/blocksync-hard-limit-cap' into mcrakhman/bloc…
mcrakhman 19d1101
refactor: block stats
mcrakhman f59580f
fix: reset timeout on getting already committed block
mcrakhman b00f0fc
refactor: review comments
mcrakhman File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,144 @@ | ||
| package blocksync | ||
|
|
||
| import ( | ||
| "time" | ||
| ) | ||
|
|
||
| // poolConfig holds configuration for block pool parameter calculations | ||
| type poolConfig struct { | ||
| // Border values for dynamic retry timer calculation | ||
| minBlockSizeBytes float64 | ||
| maxBlockSizeBytes float64 | ||
| minRetrySeconds float64 | ||
| maxRetrySeconds float64 | ||
|
|
||
| // Border values for dynamic maxPendingRequestsPerPeer | ||
| maxPendingForSmallBlocks int | ||
| maxPendingForLargeBlocks int | ||
|
|
||
| // Minimum samples before using dynamic values | ||
| minSamplesForDynamic int | ||
|
|
||
| // Default values when not enough samples | ||
| defaultMaxPendingPerPeer int | ||
| defaultRetrySeconds float64 | ||
mcrakhman marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| // Maximum memory to use for pending block requests | ||
| maxMemoryForRequesters float64 | ||
|
|
||
| // ladder step to which we round the value of requesters, e.g. 31 will be rounded to 30 | ||
| // if step is 5 | ||
| step int | ||
rach-id marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| // BlockPoolParams holds dynamically calculated parameters for the block pool | ||
| type BlockPoolParams struct { | ||
| config poolConfig | ||
| maxPendingPerPeer int | ||
| retryTimeout time.Duration | ||
| requestersLimit int | ||
| blockSizeBuffer *RotatingBuffer | ||
| maxRequesters int | ||
|
|
||
| // Cached calculated values for logging | ||
rach-id marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| avgBlockSize float64 // kept for logging purposes | ||
mcrakhman marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| maxBlockSize float64 // used for calculations | ||
rach-id marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| peerBasedLimit int | ||
| memoryBasedLimit int | ||
| numSamples int | ||
| } | ||
|
|
||
| // NewBlockPoolParams creates a new BlockPoolParams with the given configuration | ||
| func NewBlockPoolParams(config poolConfig, blockSizeBuffer *RotatingBuffer, maxRequesters int) *BlockPoolParams { | ||
| params := &BlockPoolParams{ | ||
| config: config, | ||
| blockSizeBuffer: blockSizeBuffer, | ||
| maxRequesters: maxRequesters, | ||
| } | ||
| params.recalculate(0) | ||
| return params | ||
| } | ||
|
|
||
| // recalculate updates all parameters based on current conditions | ||
| // numPeers is passed in since it's external state from BlockPool | ||
| func (p *BlockPoolParams) recalculate(numPeers int) { | ||
| p.avgBlockSize = p.blockSizeBuffer.GetAverage() | ||
| p.maxBlockSize = p.blockSizeBuffer.GetMax() | ||
| p.numSamples = p.blockSizeBuffer.Size() | ||
|
|
||
| // Use defaults if not enough samples | ||
| if p.numSamples < p.config.minSamplesForDynamic { | ||
| p.maxPendingPerPeer = p.config.defaultMaxPendingPerPeer | ||
| p.retryTimeout = time.Duration(p.config.defaultRetrySeconds * float64(time.Second)) | ||
| } else { | ||
| // Use max block size for calculations (worst-case planning) | ||
| p.maxPendingPerPeer = p.calculateMaxPendingLadder(p.maxBlockSize) | ||
| p.retryTimeout = p.calculateRetryTimeout(p.maxBlockSize) | ||
| } | ||
|
|
||
| // Calculate requesters limit based on max block size (worst-case memory usage) | ||
| p.peerBasedLimit = numPeers * p.maxPendingPerPeer | ||
| p.memoryBasedLimit = p.maxRequesters | ||
| if p.maxBlockSize > 0 { | ||
| p.memoryBasedLimit = int(p.config.maxMemoryForRequesters / p.maxBlockSize) | ||
| } | ||
| p.requestersLimit = min(p.peerBasedLimit, p.memoryBasedLimit, p.maxRequesters) | ||
| } | ||
|
|
||
| // addBlock updates parameters after adding a new block | ||
| // This triggers recalculation of all dynamic parameters based on the max block size | ||
| func (p *BlockPoolParams) addBlock(blockSize int, numPeers int) { | ||
| // Track block size | ||
| p.blockSizeBuffer.Add(float64(blockSize)) | ||
rach-id marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| // Recalculate all parameters with new max | ||
| p.recalculate(numPeers) | ||
| } | ||
|
|
||
| // calculateMaxPendingLadder returns maxPending in discrete steps (ladder effect) | ||
| // Returns values in steps of 5: 40, 35, 30, 25, 20, 15, 10, 5, 2 | ||
rach-id marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| func (p *BlockPoolParams) calculateMaxPendingLadder(blockSize float64) int { | ||
| // Clamp to min/max bounds | ||
| if blockSize <= p.config.minBlockSizeBytes { | ||
| return p.config.maxPendingForSmallBlocks | ||
| } | ||
| if blockSize >= p.config.maxBlockSizeBytes { | ||
| return p.config.maxPendingForLargeBlocks | ||
| } | ||
|
|
||
| // Calculate normalized position [0, 1] in the range | ||
| normalized := (blockSize - p.config.minBlockSizeBytes) / (p.config.maxBlockSizeBytes - p.config.minBlockSizeBytes) | ||
|
|
||
| // Inverse linear: as block size increases, max pending decreases | ||
| rawMaxPending := p.config.maxPendingForSmallBlocks - int(float64(p.config.maxPendingForSmallBlocks-p.config.maxPendingForLargeBlocks)*normalized) | ||
|
|
||
| // Round to nearest step of 5 for ladder effect | ||
| step := p.config.step | ||
| maxPending := ((rawMaxPending + step/2) / step) * step | ||
|
|
||
| // Ensure we don't go below minimum | ||
| if maxPending < p.config.maxPendingForLargeBlocks { | ||
| maxPending = p.config.maxPendingForLargeBlocks | ||
| } | ||
|
|
||
| return maxPending | ||
| } | ||
|
|
||
| // calculateRetryTimeout returns retry timeout based on block size | ||
| func (p *BlockPoolParams) calculateRetryTimeout(blockSize float64) time.Duration { | ||
| // Clamp to min/max bounds | ||
| if blockSize <= p.config.minBlockSizeBytes { | ||
| return time.Duration(p.config.minRetrySeconds * float64(time.Second)) | ||
| } | ||
| if blockSize >= p.config.maxBlockSizeBytes { | ||
| return time.Duration(p.config.maxRetrySeconds * float64(time.Second)) | ||
| } | ||
|
|
||
| // Calculate normalized position [0, 1] in the range | ||
| normalized := (blockSize - p.config.minBlockSizeBytes) / (p.config.maxBlockSizeBytes - p.config.minBlockSizeBytes) | ||
|
|
||
| // Calculate retry seconds | ||
| retrySeconds := p.config.minRetrySeconds + (p.config.maxRetrySeconds-p.config.minRetrySeconds)*normalized | ||
|
|
||
| return time.Duration(retrySeconds * float64(time.Second)) | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.