From a09b850b3908a3de8ad845d8527c8bd28b8a6bd7 Mon Sep 17 00:00:00 2001 From: ljedrz Date: Thu, 11 Jul 2024 16:38:23 +0200 Subject: [PATCH 1/3] perf: use only raw iterators with rocksdb Signed-off-by: ljedrz --- .../store/src/helpers/rocksdb/internal/map.rs | 56 ++++++++--------- .../helpers/rocksdb/internal/nested_map.rs | 62 +++++++++---------- 2 files changed, 57 insertions(+), 61 deletions(-) diff --git a/ledger/store/src/helpers/rocksdb/internal/map.rs b/ledger/store/src/helpers/rocksdb/internal/map.rs index 54cb688294..cf389bece2 100644 --- a/ledger/store/src/helpers/rocksdb/internal/map.rs +++ b/ledger/store/src/helpers/rocksdb/internal/map.rs @@ -400,7 +400,7 @@ pub struct Iter< K: 'a + Debug + PartialEq + Eq + Hash + Serialize + DeserializeOwned, V: 'a + PartialEq + Eq + Serialize + DeserializeOwned, > { - db_iter: rocksdb::DBIterator<'a>, + db_iter: rocksdb::DBRawIterator<'a>, _phantom: PhantomData<(K, V)>, } @@ -411,7 +411,7 @@ impl< > Iter<'a, K, V> { pub(super) fn new(db_iter: rocksdb::DBIterator<'a>) -> Self { - Self { db_iter, _phantom: PhantomData } + Self { db_iter: db_iter.into(), _phantom: PhantomData } } } @@ -424,13 +424,11 @@ impl< type Item = (Cow<'a, K>, Cow<'a, V>); fn next(&mut self) -> Option { - let (key, value) = self - .db_iter - .next()? - .map_err(|e| { - error!("RocksDB Iter iterator error: {e}"); - }) - .ok()?; + if !self.db_iter.valid() { + return None; + } + + let (key, value) = self.db_iter.item()?; // Deserialize the key and value. let key = bincode::deserialize(&key[PREFIX_LEN..]) @@ -438,25 +436,27 @@ impl< error!("RocksDB Iter deserialize(key) error: {e}"); }) .ok()?; - let value = bincode::deserialize(&value) + let value = bincode::deserialize(value) .map_err(|e| { error!("RocksDB Iter deserialize(value) error: {e}"); }) .ok()?; + self.db_iter.next(); + Some((Cow::Owned(key), Cow::Owned(value))) } } /// An iterator over the keys of a prefix. pub struct Keys<'a, K: 'a + Debug + PartialEq + Eq + Hash + Serialize + DeserializeOwned> { - db_iter: rocksdb::DBIterator<'a>, + db_iter: rocksdb::DBRawIterator<'a>, _phantom: PhantomData, } impl<'a, K: 'a + Debug + PartialEq + Eq + Hash + Serialize + DeserializeOwned> Keys<'a, K> { pub(crate) fn new(db_iter: rocksdb::DBIterator<'a>) -> Self { - Self { db_iter, _phantom: PhantomData } + Self { db_iter: db_iter.into(), _phantom: PhantomData } } } @@ -464,34 +464,32 @@ impl<'a, K: 'a + Clone + Debug + PartialEq + Eq + Hash + Serialize + Deserialize type Item = Cow<'a, K>; fn next(&mut self) -> Option { - let (key, _) = self - .db_iter - .next()? - .map_err(|e| { - error!("RocksDB Keys iterator error: {e}"); - }) - .ok()?; + if !self.db_iter.valid() { + return None; + } // Deserialize the key. - let key = bincode::deserialize(&key[PREFIX_LEN..]) + let key = bincode::deserialize(&self.db_iter.key()?[PREFIX_LEN..]) .map_err(|e| { error!("RocksDB Keys deserialize(key) error: {e}"); }) .ok()?; + self.db_iter.next(); + Some(Cow::Owned(key)) } } /// An iterator over the values of a prefix. pub struct Values<'a, V: 'a + PartialEq + Eq + Serialize + DeserializeOwned> { - db_iter: rocksdb::DBIterator<'a>, + db_iter: rocksdb::DBRawIterator<'a>, _phantom: PhantomData, } impl<'a, V: 'a + PartialEq + Eq + Serialize + DeserializeOwned> Values<'a, V> { pub(crate) fn new(db_iter: rocksdb::DBIterator<'a>) -> Self { - Self { db_iter, _phantom: PhantomData } + Self { db_iter: db_iter.into(), _phantom: PhantomData } } } @@ -499,21 +497,19 @@ impl<'a, V: 'a + Clone + PartialEq + Eq + Serialize + DeserializeOwned> Iterator type Item = Cow<'a, V>; fn next(&mut self) -> Option { - let (_, value) = self - .db_iter - .next()? - .map_err(|e| { - error!("RocksDB Values iterator error: {e}"); - }) - .ok()?; + if !self.db_iter.valid() { + return None; + } // Deserialize the value. - let value = bincode::deserialize(&value) + let value = bincode::deserialize(self.db_iter.value()?) .map_err(|e| { error!("RocksDB Values deserialize(value) error: {e}"); }) .ok()?; + self.db_iter.next(); + Some(Cow::Owned(value)) } } diff --git a/ledger/store/src/helpers/rocksdb/internal/nested_map.rs b/ledger/store/src/helpers/rocksdb/internal/nested_map.rs index 6fc86c4ea9..beaad74583 100644 --- a/ledger/store/src/helpers/rocksdb/internal/nested_map.rs +++ b/ledger/store/src/helpers/rocksdb/internal/nested_map.rs @@ -594,7 +594,7 @@ pub struct NestedIter< K: 'a + Debug + PartialEq + Eq + Serialize + DeserializeOwned, V: 'a + PartialEq + Eq + Serialize + DeserializeOwned, > { - db_iter: rocksdb::DBIterator<'a>, + db_iter: rocksdb::DBRawIterator<'a>, _phantom: PhantomData<(M, K, V)>, } @@ -606,7 +606,7 @@ impl< > NestedIter<'a, M, K, V> { pub(super) fn new(db_iter: rocksdb::DBIterator<'a>) -> Self { - Self { db_iter, _phantom: PhantomData } + Self { db_iter: db_iter.into(), _phantom: PhantomData } } } @@ -620,16 +620,14 @@ impl< type Item = (Cow<'a, M>, Cow<'a, K>, Cow<'a, V>); fn next(&mut self) -> Option { - let (map_key, value) = self - .db_iter - .next()? - .map_err(|e| { - error!("RocksDB NestedIter iterator error: {e}"); - }) - .ok()?; + if !self.db_iter.valid() { + return None; + } + + let (map_key, value) = self.db_iter.item()?; // Extract the bytes belonging to the map and the key. - let (entry_map, entry_key) = get_map_and_key(&map_key) + let (entry_map, entry_key) = get_map_and_key(map_key) .map_err(|e| { error!("RocksDB NestedIter get_map_and_key error: {e}"); }) @@ -647,12 +645,14 @@ impl< }) .ok()?; // Deserialize the value. - let value = bincode::deserialize(&value) + let value = bincode::deserialize(value) .map_err(|e| { error!("RocksDB NestedIter deserialize(value) error: {e}"); }) .ok()?; + self.db_iter.next(); + Some((Cow::Owned(map), Cow::Owned(key), Cow::Owned(value))) } } @@ -663,7 +663,7 @@ pub struct NestedKeys< M: 'a + Clone + Debug + PartialEq + Eq + Hash + Serialize + DeserializeOwned, K: 'a + Clone + Debug + PartialEq + Eq + Serialize + DeserializeOwned, > { - db_iter: rocksdb::DBIterator<'a>, + db_iter: rocksdb::DBRawIterator<'a>, _phantom: PhantomData<(M, K)>, } @@ -674,7 +674,7 @@ impl< > NestedKeys<'a, M, K> { pub(crate) fn new(db_iter: rocksdb::DBIterator<'a>) -> Self { - Self { db_iter, _phantom: PhantomData } + Self { db_iter: db_iter.into(), _phantom: PhantomData } } } @@ -687,16 +687,14 @@ impl< type Item = (Cow<'a, M>, Cow<'a, K>); fn next(&mut self) -> Option { - let (map_key, _) = self - .db_iter - .next()? - .map_err(|e| { - error!("RocksDB NestedKeys iterator error: {e}"); - }) - .ok()?; + if !self.db_iter.valid() { + return None; + } + + let map_key = self.db_iter.key()?; // Extract the bytes belonging to the map and the key. - let (entry_map, entry_key) = get_map_and_key(&map_key) + let (entry_map, entry_key) = get_map_and_key(map_key) .map_err(|e| { error!("RocksDB NestedKeys get_map_and_key error: {e}"); }) @@ -714,19 +712,21 @@ impl< }) .ok()?; + self.db_iter.next(); + Some((Cow::Owned(map), Cow::Owned(key))) } } /// An iterator over the values of a prefix. pub struct NestedValues<'a, V: 'a + PartialEq + Eq + Serialize + DeserializeOwned> { - db_iter: rocksdb::DBIterator<'a>, + db_iter: rocksdb::DBRawIterator<'a>, _phantom: PhantomData, } impl<'a, V: 'a + PartialEq + Eq + Serialize + DeserializeOwned> NestedValues<'a, V> { pub(crate) fn new(db_iter: rocksdb::DBIterator<'a>) -> Self { - Self { db_iter, _phantom: PhantomData } + Self { db_iter: db_iter.into(), _phantom: PhantomData } } } @@ -734,21 +734,21 @@ impl<'a, V: 'a + Clone + PartialEq + Eq + Serialize + DeserializeOwned> Iterator type Item = Cow<'a, V>; fn next(&mut self) -> Option { - let (_, value) = self - .db_iter - .next()? - .map_err(|e| { - error!("RocksDB NestedValues iterator error: {e}"); - }) - .ok()?; + if !self.db_iter.valid() { + return None; + } + + let value = self.db_iter.value()?; // Deserialize the value. - let value = bincode::deserialize(&value) + let value = bincode::deserialize(value) .map_err(|e| { error!("RocksDB NestedValues deserialize(value) error: {e}"); }) .ok()?; + self.db_iter.next(); + Some(Cow::Owned(value)) } } From f8fc68ca22ce9369f3e82107c48f3738bf00af50 Mon Sep 17 00:00:00 2001 From: ljedrz Date: Fri, 12 Jul 2024 10:06:32 +0200 Subject: [PATCH 2/3] fix: make len_confirmed work on empty data sets Signed-off-by: ljedrz --- .../store/src/helpers/rocksdb/internal/map.rs | 22 ++++++++++------- .../helpers/rocksdb/internal/nested_map.rs | 24 +++++++++++-------- 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/ledger/store/src/helpers/rocksdb/internal/map.rs b/ledger/store/src/helpers/rocksdb/internal/map.rs index cf389bece2..df5daf9cc4 100644 --- a/ledger/store/src/helpers/rocksdb/internal/map.rs +++ b/ledger/store/src/helpers/rocksdb/internal/map.rs @@ -276,17 +276,21 @@ impl< // Count the number of keys belonging to the map. let mut len = 0usize; - while let Some(key) = iter.key() { - // Only compare the map ID - the network ID is guaranteed to - // remain the same as long as there is more than a single map. - if key[2..][..2] != self.context[2..][..2] { - // If the map ID is different, it's the end of iteration. + while iter.valid() { + if let Some(key) = iter.key() { + // Only compare the map ID - the network ID is guaranteed to + // remain the same as long as there is more than a single map. + if key[2..][..2] != self.context[2..][..2] { + // If the map ID is different, it's the end of iteration. + break; + } + + // Increment the length and go to the next record. + len += 1; + iter.next(); + } else { break; } - - // Increment the length and go to the next record. - len += 1; - iter.next(); } len diff --git a/ledger/store/src/helpers/rocksdb/internal/nested_map.rs b/ledger/store/src/helpers/rocksdb/internal/nested_map.rs index beaad74583..036845fe37 100644 --- a/ledger/store/src/helpers/rocksdb/internal/nested_map.rs +++ b/ledger/store/src/helpers/rocksdb/internal/nested_map.rs @@ -371,18 +371,22 @@ impl< // Count the number of keys belonging to the nested map. let mut len = 0usize; - while let Some(key) = iter.key() { - // Only compare the nested map - the network ID and the outer map - // ID are guaranteed to remain the same as long as there is more - // than a single map in the database. - if !key[PREFIX_LEN + 4..].starts_with(serialized_map) { - // If the nested map ID is different, it's the end of iteration. + while iter.valid() { + if let Some(key) = iter.key() { + // Only compare the nested map - the network ID and the outer map + // ID are guaranteed to remain the same as long as there is more + // than a single map in the database. + if !key[PREFIX_LEN + 4..].starts_with(serialized_map) { + // If the nested map ID is different, it's the end of iteration. + break; + } + + // Increment the length and go to the next record. + len += 1; + iter.next(); + } else { break; } - - // Increment the length and go to the next record. - len += 1; - iter.next(); } Ok(len) From 1dbf87418379118a74c2f53fd9f45b444c6c2d6b Mon Sep 17 00:00:00 2001 From: ljedrz Date: Thu, 11 Jul 2024 16:42:46 +0200 Subject: [PATCH 3/3] perf: speed up the BlockStore Merkle tree creation Signed-off-by: ljedrz --- ledger/store/src/block/mod.rs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/ledger/store/src/block/mod.rs b/ledger/store/src/block/mod.rs index c3cccce3cf..745ff1d805 100644 --- a/ledger/store/src/block/mod.rs +++ b/ledger/store/src/block/mod.rs @@ -1015,17 +1015,10 @@ impl> BlockStore { // Compute the block tree. let tree = { // Prepare an iterator over the block heights. - let heights = storage.id_map().keys_confirmed(); + let mut heights_hashes = storage.id_map().iter_confirmed().collect::>(); + heights_hashes.sort_unstable_by(|(h1, _), (h2, _)| h1.cmp(h2)); // Prepare the leaves of the block tree. - let hashes = match heights.max() { - Some(height) => cfg_into_iter!(0..=cow_to_copied!(height)) - .map(|height| match storage.get_block_hash(height)? { - Some(hash) => Ok(hash.to_bits_le()), - None => bail!("Missing block hash for block {height}"), - }) - .collect::>>>()?, - None => vec![], - }; + let hashes = cfg_into_iter!(heights_hashes).map(|(_, hash)| hash.to_bits_le()).collect::>>(); // Construct the block tree. Arc::new(RwLock::new(N::merkle_tree_bhp(&hashes)?)) };