zebrad: check state in obtaintips, not extendtips.

The original sync algorithm split the sync process into two phases, one
that obtained prospective chain tips, and another that attempted to
extend those chain tips as far as possible until encountering an error
(at which point the prospective state is discarded and the process
restarts).

Because a previous implementation of this algorithm didn't properly
enforce linkage between segments of the chain while extending tips,
sometimes it would get confused and fail to discard responses that did
not extend a tip.  To mitigate this, a check against the state was
added.  However, this check can cause stalls while checkpointing,
because when a checkpoint is reached we may suddenly need to commit
thousands of blocks to the state.  Because the sync algorithm now has a
a `CheckedTip` structure that ensures that a new segment of hashes
actually extends an existing one, we don't need to check against the
state while extending a tip, because we don't get confused while
interpreting responses.

This change results in significantly smoother progress on mainnet.
This commit is contained in:
Henry de Valence 2020-10-22 10:58:49 -07:00
parent 65e0c22fbe
commit 0a405c737d
1 changed files with 10 additions and 12 deletions

View File

@ -368,8 +368,14 @@ where
tracing::debug!(?self.prospective_tips);
self.request_blocks(download_set.into_iter().collect())
.await?;
// Check that the new tips we got are actually unknown.
for hash in &download_set {
tracing::debug!(?hash, "checking if state contains hash");
if self.state_contains(*hash).await? {
return Err(eyre!("queued download of hash behind our chain tip"));
}
}
self.request_blocks(download_set).await?;
Ok(())
}
@ -488,8 +494,7 @@ where
}
}
self.request_blocks(download_set.into_iter().collect())
.await?;
self.request_blocks(download_set).await?;
Ok(())
}
@ -521,16 +526,9 @@ where
}
/// Queue download and verify tasks for each block that isn't currently known to our node
async fn request_blocks(&mut self, hashes: Vec<block::Hash>) -> Result<(), Report> {
async fn request_blocks(&mut self, hashes: HashSet<block::Hash>) -> Result<(), Report> {
tracing::debug!(hashes.len = hashes.len(), "requesting blocks");
for hash in hashes.into_iter() {
// If we've queued the download of a hash behind our current chain tip,
// we've been given bad responses by our peers. Abort the sync and restart.
tracing::debug!(?hash, "checking if state contains hash");
if self.state_contains(hash).await? {
return Err(eyre!("queued download of hash behind our chain tip"));
}
self.downloads
.download_and_verify(hash)
.await