Skip to content

Commit

Permalink
Optimize Option to Error conversion (#3036)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
hashmap authored Sep 10, 2019
1 parent 4faac47 commit 80a8f76
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 26 deletions.
40 changes: 24 additions & 16 deletions chain/src/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand All @@ -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),
)
}

Expand All @@ -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),
)
}

Expand All @@ -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),
)
}

Expand Down Expand Up @@ -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),
)
}

Expand All @@ -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).
Expand All @@ -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.
Expand Down Expand Up @@ -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),
)
}

Expand Down Expand Up @@ -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),
)
}

Expand Down Expand Up @@ -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),
)
}

Expand Down Expand Up @@ -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),
)
}

Expand Down
15 changes: 7 additions & 8 deletions p2p/src/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down Expand Up @@ -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();
Expand Down
7 changes: 5 additions & 2 deletions store/src/lmdb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
}
Expand Down

0 comments on commit 80a8f76

Please sign in to comment.