limit fork choice initialization horizon (#1910)

This helps manage the long startup time on chains that are not
finalizing for long periods of time
This commit is contained in:
Jacek Sieka 2020-10-29 12:09:03 +01:00 committed by GitHub
parent 020a32ffa4
commit 81f4fe0783
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 41 additions and 8 deletions

View File

@ -41,22 +41,46 @@ proc init*(T: type AttestationPool, chainDag: ChainDAGRef, quarantine: Quarantin
var blocks: seq[BlockRef] var blocks: seq[BlockRef]
var cur = chainDag.head var cur = chainDag.head
# When the chain is finalizing, the votes between the head block and the
# finalized checkpoint should be enough for a stable fork choice - when the
# chain is not finalizing, we want to seed it with as many votes as possible
# since the whole history of each branch might be significant. It is however
# a game of diminishing returns, and we have to weigh it against the time
# it takes to replay that many blocks during startup and thus miss _new_
# votes.
const ForkChoiceHorizon = 256
while cur != chainDag.finalizedHead.blck: while cur != chainDag.finalizedHead.blck:
blocks.add cur blocks.add cur
cur = cur.parent cur = cur.parent
debug "Preloading fork choice with blocks", blocks = blocks.len info "Initializing fork choice from block database",
unfinalized_blocks = blocks.len
for blck in reversed(blocks): var epochRef = finalizedEpochRef
for i in 0..<blocks.len:
let let
epochRef = chainDag.getEpochRef(blck, blck.slot.compute_epoch_at_slot) blck = blocks[blocks.len - i - 1]
status = status =
forkChoice.process_block( if i > (blocks.len - ForkChoiceHorizon) or (i mod 1024 != 0):
chainDag, epochRef, blck, chainDag.get(blck).data.message, blck.slot) # Fork choice needs to know about the full block tree up to the
# finalization point, but doesn't really need to have overly accurate
# justification and finalization points until we get close to head -
# nonetheless, we'll make sure to pass a fresh finalization point now
# and then to make sure the fork choice data structure doesn't grow
# too big - getting an EpochRef can be expensive.
forkChoice.backend.process_block(
blck.root, blck.parent.root,
epochRef.current_justified_checkpoint.epoch,
epochRef.finalized_checkpoint.epoch)
else:
epochRef = chainDag.getEpochRef(blck, blck.slot.epoch)
forkChoice.process_block(
chainDag, epochRef, blck, chainDag.get(blck).data.message, blck.slot)
doAssert status.isOk(), "Error in preloading the fork choice: " & $status.error doAssert status.isOk(), "Error in preloading the fork choice: " & $status.error
debug "Fork choice initialized", info "Fork choice initialized",
justified_epoch = chainDag.headState.data.data.current_justified_checkpoint.epoch, justified_epoch = chainDag.headState.data.data.current_justified_checkpoint.epoch,
finalized_epoch = chainDag.headState.data.data.finalized_checkpoint.epoch, finalized_epoch = chainDag.headState.data.data.finalized_checkpoint.epoch,
finalized_root = shortlog(chainDag.finalizedHead.blck.root) finalized_root = shortlog(chainDag.finalizedHead.blck.root)

View File

@ -35,6 +35,15 @@ proc parseBootstrapAddress*(address: TaintedString):
else: else:
return err "Ignoring unrecognized bootstrap address type" return err "Ignoring unrecognized bootstrap address type"
iterator strippedLines(filename: string): string {.raises: [ref IOError].} =
for line in lines(filename):
let stripped = strip(line)
if stripped.startsWith('#'): # Comments
continue
if stripped.len > 0:
yield stripped
proc addBootstrapNode*(bootstrapAddr: string, proc addBootstrapNode*(bootstrapAddr: string,
bootstrapEnrs: var seq[enr.Record]) = bootstrapEnrs: var seq[enr.Record]) =
# Ignore empty lines or lines starting with # # Ignore empty lines or lines starting with #
@ -54,7 +63,7 @@ proc loadBootstrapFile*(bootstrapFile: string,
let ext = splitFile(bootstrapFile).ext let ext = splitFile(bootstrapFile).ext
if cmpIgnoreCase(ext, ".txt") == 0 or cmpIgnoreCase(ext, ".enr") == 0 : if cmpIgnoreCase(ext, ".txt") == 0 or cmpIgnoreCase(ext, ".enr") == 0 :
try: try:
for ln in lines(bootstrapFile): for ln in strippedLines(bootstrapFile):
addBootstrapNode(ln, bootstrapEnrs) addBootstrapNode(ln, bootstrapEnrs)
except IOError as e: except IOError as e:
error "Could not read bootstrap file", msg = e.msg error "Could not read bootstrap file", msg = e.msg
@ -64,7 +73,7 @@ proc loadBootstrapFile*(bootstrapFile: string,
# TODO. This is very ugly, but let's try to negotiate the # TODO. This is very ugly, but let's try to negotiate the
# removal of YAML metadata. # removal of YAML metadata.
try: try:
for ln in lines(bootstrapFile): for ln in strippedLines(bootstrapFile):
addBootstrapNode(string(ln.strip()[3..^2]), bootstrapEnrs) addBootstrapNode(string(ln.strip()[3..^2]), bootstrapEnrs)
except IOError as e: except IOError as e:
error "Could not read bootstrap file", msg = e.msg error "Could not read bootstrap file", msg = e.msg