Skip to content

Commit a53b568

Browse files
authored
Turn block heights list into ranges to load more at once from storage. (#4880)
## Motivation When handling a chain info query asking for sender certificate hashes, we currently load those hashes from the chain state view one by one: `chain.block_hashes(height..=height).await?`. In most cases, the block heights are actually contiguous. ## Proposal Turn the list into an equivalent list of ranges, and load each range at once. ## Test Plan CI should catch any regressions. We should find a way to try out if this improves performance, or even addresses the problems on the testnet. ## Release Plan - These changes should be backported to `testnet_conway`, then - be released in a new SDK, - be released in a validator hotfix. ## Links - [reviewer checklist](https://github.com/linera-io/linera-protocol/blob/main/CONTRIBUTING.md#reviewer-checklist)
1 parent 193a002 commit a53b568

File tree

1 file changed

+37
-2
lines changed

1 file changed

+37
-2
lines changed

linera-core/src/chain_worker/state.rs

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use std::{
77
borrow::Cow,
88
collections::{BTreeMap, BTreeSet, HashMap, HashSet},
99
iter,
10+
ops::RangeInclusive,
1011
sync::{self, Arc},
1112
};
1213

@@ -1571,8 +1572,9 @@ where
15711572
info.requested_pending_message_bundles = bundles;
15721573
}
15731574
let mut hashes = Vec::new();
1574-
for height in query.request_sent_certificate_hashes_by_heights {
1575-
hashes.extend(chain.block_hashes(height..=height).await?);
1575+
let height_ranges = into_ranges(query.request_sent_certificate_hashes_by_heights);
1576+
for height_range in height_ranges {
1577+
hashes.extend(chain.block_hashes(height_range).await?);
15761578
}
15771579
info.requested_sent_certificate_hashes = hashes;
15781580
if let Some(start) = query.request_received_log_excluding_first_n {
@@ -1650,6 +1652,21 @@ where
16501652
}
16511653
}
16521654

1655+
/// Returns an iterator of inclusive ranges that exactly cover the list of block heights.
1656+
fn into_ranges(
1657+
values: impl IntoIterator<Item = BlockHeight>,
1658+
) -> impl Iterator<Item = RangeInclusive<BlockHeight>> {
1659+
let mut values_iter = values.into_iter().peekable();
1660+
iter::from_fn(move || {
1661+
let start = values_iter.next()?;
1662+
let mut end = start;
1663+
while values_iter.peek() == end.try_add_one().ok().as_ref() {
1664+
end = values_iter.next()?;
1665+
}
1666+
Some(start..=end)
1667+
})
1668+
}
1669+
16531670
/// Returns the keys whose value is `None`.
16541671
fn missing_blob_ids(maybe_blobs: &BTreeMap<BlobId, Option<Blob>>) -> Vec<BlobId> {
16551672
maybe_blobs
@@ -1762,3 +1779,21 @@ impl<'a> CrossChainUpdateHelper<'a> {
17621779
Ok(bundles)
17631780
}
17641781
}
1782+
1783+
#[test]
1784+
fn test_into_ranges() {
1785+
assert_eq!(
1786+
into_ranges(vec![
1787+
BlockHeight(2),
1788+
BlockHeight(3),
1789+
BlockHeight(4),
1790+
BlockHeight(6)
1791+
],)
1792+
.collect::<Vec<_>>(),
1793+
vec![
1794+
BlockHeight(2)..=BlockHeight(4),
1795+
BlockHeight(6)..=BlockHeight(6)
1796+
]
1797+
);
1798+
assert_eq!(into_ranges(vec![]).collect::<Vec<_>>(), vec![]);
1799+
}

0 commit comments

Comments
 (0)