Skip to content

Commit 65a0d61

Browse files
authored
Turn block heights list into ranges to load more at once from storage. (#4881)
Backport of #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 released in a new SDK, - be released in a validator hotfix. ## Links - PR to main: #4880 - [reviewer checklist](https://github.com/linera-io/linera-protocol/blob/main/CONTRIBUTING.md#reviewer-checklist)
1 parent 9773202 commit 65a0d61

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

@@ -1517,8 +1518,9 @@ where
15171518
info.requested_pending_message_bundles = bundles;
15181519
}
15191520
let mut hashes = Vec::new();
1520-
for height in query.request_sent_certificate_hashes_by_heights {
1521-
hashes.extend(chain.block_hashes(height..=height).await?);
1521+
let height_ranges = into_ranges(query.request_sent_certificate_hashes_by_heights);
1522+
for height_range in height_ranges {
1523+
hashes.extend(chain.block_hashes(height_range).await?);
15221524
}
15231525
info.requested_sent_certificate_hashes = hashes;
15241526
if let Some(start) = query.request_received_log_excluding_first_n {
@@ -1596,6 +1598,21 @@ where
15961598
}
15971599
}
15981600

1601+
/// Returns an iterator of inclusive ranges that exactly cover the list of block heights.
1602+
fn into_ranges(
1603+
values: impl IntoIterator<Item = BlockHeight>,
1604+
) -> impl Iterator<Item = RangeInclusive<BlockHeight>> {
1605+
let mut values_iter = values.into_iter().peekable();
1606+
iter::from_fn(move || {
1607+
let start = values_iter.next()?;
1608+
let mut end = start;
1609+
while values_iter.peek() == end.try_add_one().ok().as_ref() {
1610+
end = values_iter.next()?;
1611+
}
1612+
Some(start..=end)
1613+
})
1614+
}
1615+
15991616
/// Returns the keys whose value is `None`.
16001617
fn missing_blob_ids(maybe_blobs: &BTreeMap<BlobId, Option<Blob>>) -> Vec<BlobId> {
16011618
maybe_blobs
@@ -1708,3 +1725,21 @@ impl<'a> CrossChainUpdateHelper<'a> {
17081725
Ok(bundles)
17091726
}
17101727
}
1728+
1729+
#[test]
1730+
fn test_into_ranges() {
1731+
assert_eq!(
1732+
into_ranges(vec![
1733+
BlockHeight(2),
1734+
BlockHeight(3),
1735+
BlockHeight(4),
1736+
BlockHeight(6)
1737+
],)
1738+
.collect::<Vec<_>>(),
1739+
vec![
1740+
BlockHeight(2)..=BlockHeight(4),
1741+
BlockHeight(6)..=BlockHeight(6)
1742+
]
1743+
);
1744+
assert_eq!(into_ranges(vec![]).collect::<Vec<_>>(), vec![]);
1745+
}

0 commit comments

Comments
 (0)