Optimize Option to Error conversion (#3036)

To convert option to error we generate an error message. In some places
it contains header or block hash code or other data which is costly to
produce. So during the initial header sync we spend 12% of all time on
generating those messages (in 99% cases we don't use it). This PR
introduces a lazy generation of error messages which completely
eliminates CPU load during the header sync.
This commit is contained in:
hashmap 2019-09-10 14:38:36 +02:00 committed by GitHub
parent 4faac470d4
commit 80a8f76c4c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 36 additions and 26 deletions

View file

@ -54,12 +54,12 @@ impl ChainStore {
impl ChainStore {
/// The current chain head.
pub fn head(&self) -> Result<Tip, Error> {
option_to_not_found(self.db.get_ser(&vec![HEAD_PREFIX]), "HEAD")
option_to_not_found(self.db.get_ser(&vec![HEAD_PREFIX]), || "HEAD".to_owned())
}
/// The current chain "tail" (earliest block in the store).
pub fn tail(&self) -> Result<Tip, Error> {
option_to_not_found(self.db.get_ser(&vec![TAIL_PREFIX]), "TAIL")
option_to_not_found(self.db.get_ser(&vec![TAIL_PREFIX]), || "TAIL".to_owned())
}
/// Header of the block at the head of the block chain (not the same thing as header_head).
@ -69,19 +69,23 @@ impl ChainStore {
/// Head of the header chain (not the same thing as head_header).
pub fn header_head(&self) -> Result<Tip, Error> {
option_to_not_found(self.db.get_ser(&vec![HEADER_HEAD_PREFIX]), "HEADER_HEAD")
option_to_not_found(self.db.get_ser(&vec![HEADER_HEAD_PREFIX]), || {
"HEADER_HEAD".to_owned()
})
}
/// The "sync" head.
pub fn get_sync_head(&self) -> Result<Tip, Error> {
option_to_not_found(self.db.get_ser(&vec![SYNC_HEAD_PREFIX]), "SYNC_HEAD")
option_to_not_found(self.db.get_ser(&vec![SYNC_HEAD_PREFIX]), || {
"SYNC_HEAD".to_owned()
})
}
/// Get full block.
pub fn get_block(&self, h: &Hash) -> Result<Block, Error> {
option_to_not_found(
self.db.get_ser(&to_key(BLOCK_PREFIX, &mut h.to_vec())),
&format!("BLOCK: {}", h),
|| format!("BLOCK: {}", h),
)
}
@ -94,7 +98,7 @@ impl ChainStore {
pub fn get_block_sums(&self, h: &Hash) -> Result<BlockSums, Error> {
option_to_not_found(
self.db.get_ser(&to_key(BLOCK_SUMS_PREFIX, &mut h.to_vec())),
&format!("Block sums for block: {}", h),
|| format!("Block sums for block: {}", h),
)
}
@ -108,7 +112,7 @@ impl ChainStore {
option_to_not_found(
self.db
.get_ser(&to_key(BLOCK_HEADER_PREFIX, &mut h.to_vec())),
&format!("BLOCK HEADER: {}", h),
|| format!("BLOCK HEADER: {}", h),
)
}
@ -148,7 +152,7 @@ impl ChainStore {
COMMIT_POS_HGT_PREFIX,
&mut commit.as_ref().to_vec(),
)),
&format!("Output position for: {:?}", commit),
|| format!("Output position for: {:?}", commit),
)
}
@ -169,12 +173,12 @@ pub struct Batch<'a> {
impl<'a> Batch<'a> {
/// The head.
pub fn head(&self) -> Result<Tip, Error> {
option_to_not_found(self.db.get_ser(&vec![HEAD_PREFIX]), "HEAD")
option_to_not_found(self.db.get_ser(&vec![HEAD_PREFIX]), || "HEAD".to_owned())
}
/// The tail.
pub fn tail(&self) -> Result<Tip, Error> {
option_to_not_found(self.db.get_ser(&vec![TAIL_PREFIX]), "TAIL")
option_to_not_found(self.db.get_ser(&vec![TAIL_PREFIX]), || "TAIL".to_owned())
}
/// Header of the block at the head of the block chain (not the same thing as header_head).
@ -184,12 +188,16 @@ impl<'a> Batch<'a> {
/// Head of the header chain (not the same thing as head_header).
pub fn header_head(&self) -> Result<Tip, Error> {
option_to_not_found(self.db.get_ser(&vec![HEADER_HEAD_PREFIX]), "HEADER_HEAD")
option_to_not_found(self.db.get_ser(&vec![HEADER_HEAD_PREFIX]), || {
"HEADER_HEAD".to_owned()
})
}
/// Get "sync" head.
pub fn get_sync_head(&self) -> Result<Tip, Error> {
option_to_not_found(self.db.get_ser(&vec![SYNC_HEAD_PREFIX]), "SYNC_HEAD")
option_to_not_found(self.db.get_ser(&vec![SYNC_HEAD_PREFIX]), || {
"SYNC_HEAD".to_owned()
})
}
/// Save body head to db.
@ -228,7 +236,7 @@ impl<'a> Batch<'a> {
pub fn get_block(&self, h: &Hash) -> Result<Block, Error> {
option_to_not_found(
self.db.get_ser(&to_key(BLOCK_PREFIX, &mut h.to_vec())),
&format!("Block with hash: {}", h),
|| format!("Block with hash: {}", h),
)
}
@ -316,7 +324,7 @@ impl<'a> Batch<'a> {
COMMIT_POS_HGT_PREFIX,
&mut commit.as_ref().to_vec(),
)),
&format!("Output position for commit: {:?}", commit),
|| format!("Output position for commit: {:?}", commit),
)
}
@ -348,7 +356,7 @@ impl<'a> Batch<'a> {
option_to_not_found(
self.db
.get_ser(&to_key(BLOCK_HEADER_PREFIX, &mut h.to_vec())),
&format!("BLOCK HEADER: {}", h),
|| format!("BLOCK HEADER: {}", h),
)
}
@ -376,7 +384,7 @@ impl<'a> Batch<'a> {
pub fn get_block_sums(&self, h: &Hash) -> Result<BlockSums, Error> {
option_to_not_found(
self.db.get_ser(&to_key(BLOCK_SUMS_PREFIX, &mut h.to_vec())),
&format!("Block sums for block: {}", h),
|| format!("Block sums for block: {}", h),
)
}

View file

@ -129,10 +129,9 @@ impl PeerStore {
}
pub fn get_peer(&self, peer_addr: PeerAddr) -> Result<PeerData, Error> {
option_to_not_found(
self.db.get_ser(&peer_key(peer_addr)[..]),
&format!("Peer at address: {}", peer_addr),
)
option_to_not_found(self.db.get_ser(&peer_key(peer_addr)[..]), || {
format!("Peer at address: {}", peer_addr)
})
}
pub fn exists_peer(&self, peer_addr: PeerAddr) -> Result<bool, Error> {
@ -179,10 +178,10 @@ impl PeerStore {
pub fn update_state(&self, peer_addr: PeerAddr, new_state: State) -> Result<(), Error> {
let batch = self.db.batch()?;
let mut peer = option_to_not_found(
batch.get_ser::<PeerData>(&peer_key(peer_addr)[..]),
&format!("Peer at address: {}", peer_addr),
)?;
let mut peer =
option_to_not_found(batch.get_ser::<PeerData>(&peer_key(peer_addr)[..]), || {
format!("Peer at address: {}", peer_addr)
})?;
peer.flags = new_state;
if new_state == State::Banned {
peer.last_banned = Utc::now().timestamp();

View file

@ -54,9 +54,12 @@ impl From<lmdb::error::Error> for Error {
}
/// unwraps the inner option by converting the none case to a not found error
pub fn option_to_not_found<T>(res: Result<Option<T>, Error>, field_name: &str) -> Result<T, Error> {
pub fn option_to_not_found<T, F>(res: Result<Option<T>, Error>, field_name: F) -> Result<T, Error>
where
F: Fn() -> String,
{
match res {
Ok(None) => Err(Error::NotFoundErr(field_name.to_owned())),
Ok(None) => Err(Error::NotFoundErr(field_name())),
Ok(Some(o)) => Ok(o),
Err(e) => Err(e),
}