Skip to content

Commit 272bc98

Browse files
authored
Control creation of network actions when handling ChainInfoQuery. (#4523)
## Motivation Creating network actions requires reading certificates – this takes a lot of CPU time (reading from storage, deserializing certificates, etc.) but it's not always needed. Network actions are created in couple of places but one surprising one was when handling a `ChainInfoQuery`. ## Proposal Add a boolean field to `ChainInfoQuery` struct that controls whether the caller wants to create network actions. By default it is set to `true` to maintain backwards compatibility but clients can call `no_network_actions` to set it to false. ## Test Plan CI. ## Release Plan - Nothing to do / These changes follow the usual release cycle. This is already backported to testnet (#4518) ## Links - [reviewer checklist](https://github.com/linera-io/linera-protocol/blob/main/CONTRIBUTING.md#reviewer-checklist)
1 parent 7bc5c4c commit 272bc98

File tree

7 files changed

+73
-39
lines changed

7 files changed

+73
-39
lines changed

linera-core/src/chain_worker/state.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,7 @@ where
246246
&mut self,
247247
query: ChainInfoQuery,
248248
) -> Result<(ChainInfoResponse, NetworkActions), WorkerError> {
249+
let create_network_actions = query.create_network_actions;
249250
if let Some((height, round)) = query.request_leader_timeout {
250251
self.vote_for_leader_timeout(height, round).await?;
251252
}
@@ -254,7 +255,11 @@ where
254255
}
255256
let response = self.prepare_chain_info_response(query).await?;
256257
// Trigger any outgoing cross-chain messages that haven't been confirmed yet.
257-
let actions = self.create_network_actions(None).await?;
258+
let actions = if create_network_actions {
259+
self.create_network_actions(None).await?
260+
} else {
261+
NetworkActions::default()
262+
};
258263
Ok((response, actions))
259264
}
260265

linera-core/src/data_types.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,14 @@ pub struct ChainInfoQuery {
9595
/// Query for certificate hashes at block heights.
9696
#[debug(skip_if = Vec::is_empty)]
9797
pub request_sent_certificate_hashes_by_heights: Vec<BlockHeight>,
98+
#[serde(default = "default_true")]
99+
pub create_network_actions: bool,
100+
}
101+
102+
// Default value for create_network_actions.
103+
// Default for bool returns false.
104+
fn default_true() -> bool {
105+
true
98106
}
99107

100108
impl ChainInfoQuery {
@@ -111,6 +119,7 @@ impl ChainInfoQuery {
111119
request_leader_timeout: None,
112120
request_fallback: false,
113121
request_sent_certificate_hashes_by_heights: Vec::new(),
122+
create_network_actions: false,
114123
}
115124
}
116125

@@ -158,6 +167,11 @@ impl ChainInfoQuery {
158167
self.request_fallback = true;
159168
self
160169
}
170+
171+
pub fn with_network_actions(mut self) -> Self {
172+
self.create_network_actions = true;
173+
self
174+
}
161175
}
162176

163177
#[derive(Clone, Debug, Serialize, Deserialize)]

linera-core/src/local_node.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ where
257257
let (_response, actions) = self
258258
.node
259259
.state
260-
.handle_chain_info_query(ChainInfoQuery::new(sender_chain))
260+
.handle_chain_info_query(ChainInfoQuery::new(sender_chain).with_network_actions())
261261
.await?;
262262
let mut requests = VecDeque::from_iter(actions.cross_chain_requests);
263263
while let Some(request) = requests.pop_front() {

linera-core/src/updater.rs

Lines changed: 45 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -338,11 +338,11 @@ where
338338
.await?;
339339
}
340340
Err(NodeError::MissingCrossChainUpdate { .. }) if !sent_cross_chain_updates => {
341-
sent_cross_chain_updates = true;
342341
// Some received certificates may be missing for this validator
343342
// (e.g. to create the chain or make the balance sufficient) so we are going to
344343
// synchronize them now and retry.
345344
self.send_chain_information_for_senders(chain_id).await?;
345+
sent_cross_chain_updates = true;
346346
}
347347
Err(NodeError::EventsNotFound(event_ids)) => {
348348
let mut publisher_heights = BTreeMap::new();
@@ -482,20 +482,26 @@ where
482482
let (remote_height, remote_round) = (info.next_block_height, info.manager.current_round);
483483
// Obtain the missing blocks and the manager state from the local node.
484484
let range = remote_height..target_block_height;
485-
let keys = {
486-
let chain = self.local_node.chain_state_view(chain_id).await?;
487-
chain.block_hashes(range).await?
488-
};
489-
if !keys.is_empty() {
485+
let validator_missing_hashes = self
486+
.local_node
487+
.chain_state_view(chain_id)
488+
.await?
489+
.block_hashes(range)
490+
.await?;
491+
if !validator_missing_hashes.is_empty() {
490492
// Send the requested certificates in order.
491-
let storage = self.local_node.storage_client();
492-
let certificates = storage.read_certificates(keys.clone()).await?;
493-
let certificates = match ResultReadCertificates::new(certificates, keys) {
494-
ResultReadCertificates::Certificates(certificates) => certificates,
495-
ResultReadCertificates::InvalidHashes(hashes) => {
496-
return Err(ChainClientError::ReadCertificatesError(hashes))
497-
}
498-
};
493+
let certificates = self
494+
.local_node
495+
.storage_client()
496+
.read_certificates(validator_missing_hashes.clone())
497+
.await?;
498+
let certificates =
499+
match ResultReadCertificates::new(certificates, validator_missing_hashes) {
500+
ResultReadCertificates::Certificates(certificates) => certificates,
501+
ResultReadCertificates::InvalidHashes(hashes) => {
502+
return Err(ChainClientError::ReadCertificatesError(hashes))
503+
}
504+
};
499505
for certificate in certificates {
500506
self.send_confirmed_certificate(certificate, delivery)
501507
.await?;
@@ -513,38 +519,40 @@ where
513519

514520
async fn send_chain_info_up_to_heights(
515521
&mut self,
516-
chain_heights: BTreeMap<ChainId, BlockHeight>,
522+
chain_heights: impl IntoIterator<Item = (ChainId, BlockHeight)>,
517523
delivery: CrossChainMessageDelivery,
518524
) -> Result<(), ChainClientError> {
519-
let stream =
520-
FuturesUnordered::from_iter(chain_heights.into_iter().map(|(chain_id, height)| {
521-
let mut updater = self.clone();
522-
async move {
523-
updater
524-
.send_chain_information(chain_id, height, delivery)
525-
.await
526-
}
527-
}));
528-
stream.try_collect::<Vec<_>>().await?;
525+
FuturesUnordered::from_iter(chain_heights.into_iter().map(|(chain_id, height)| {
526+
let mut updater = self.clone();
527+
async move {
528+
updater
529+
.send_chain_information(chain_id, height, delivery)
530+
.await
531+
}
532+
}))
533+
.try_collect::<Vec<_>>()
534+
.await?;
529535
Ok(())
530536
}
531537

538+
/// Updates validator with certificates for all chains that have sent messages to `chain_id`.
532539
async fn send_chain_information_for_senders(
533540
&mut self,
534541
chain_id: ChainId,
535542
) -> Result<(), ChainClientError> {
536-
let mut sender_heights = BTreeMap::new();
537-
{
538-
let chain = self.local_node.chain_state_view(chain_id).await?;
539-
let pairs = chain.inboxes.try_load_all_entries().await?;
540-
for (origin, inbox) in pairs {
541-
let inbox_next_height = inbox.next_block_height_to_receive()?;
542-
sender_heights
543-
.entry(origin)
544-
.and_modify(|h| *h = inbox_next_height.max(*h))
545-
.or_insert(inbox_next_height);
546-
}
547-
}
543+
let sender_heights = self
544+
.local_node
545+
.chain_state_view(chain_id)
546+
.await?
547+
.inboxes
548+
.try_load_all_entries()
549+
.await?
550+
.iter()
551+
.map(|(origin, inbox)| {
552+
let next_height = inbox.next_block_height_to_receive()?;
553+
Ok((*origin, next_height))
554+
})
555+
.collect::<Result<Vec<(ChainId, BlockHeight)>, ChainClientError>>()?;
548556

549557
self.send_chain_info_up_to_heights(sender_heights, CrossChainMessageDelivery::Blocking)
550558
.await?;

linera-rpc/proto/rpc.proto

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,9 @@ message ChainInfoQuery {
233233

234234
// Query for certificate hashes at block heights sent from the chain.
235235
optional bytes request_sent_certificate_hashes_by_heights = 11;
236+
237+
// Whether to create network actions as part of the query.
238+
optional bool create_network_actions = 12;
236239
}
237240

238241
// An authenticated proposal for a new block.

linera-rpc/src/grpc/conversions.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,7 @@ impl TryFrom<api::ChainInfoQuery> for ChainInfoQuery {
614614
request_fallback: chain_info_query.request_fallback,
615615
request_sent_certificate_hashes_by_heights,
616616
request_sent_certificate_hashes_in_range: None,
617+
create_network_actions: chain_info_query.create_network_actions.unwrap_or(true),
617618
})
618619
}
619620
}
@@ -644,6 +645,7 @@ impl TryFrom<ChainInfoQuery> for api::ChainInfoQuery {
644645
request_manager_values: chain_info_query.request_manager_values,
645646
request_leader_timeout,
646647
request_fallback: chain_info_query.request_fallback,
648+
create_network_actions: Some(chain_info_query.create_network_actions),
647649
})
648650
}
649651
}
@@ -1172,6 +1174,7 @@ pub mod tests {
11721174
request_fallback: true,
11731175
request_sent_certificate_hashes_by_heights: (3..8).map(BlockHeight::from).collect(),
11741176
request_sent_certificate_hashes_in_range: None,
1177+
create_network_actions: true,
11751178
};
11761179
round_trip_check::<_, api::ChainInfoQuery>(chain_info_query_some);
11771180
}

linera-rpc/tests/snapshots/format__format.yaml.snap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,7 @@ ChainInfoQuery:
365365
- request_sent_certificate_hashes_by_heights:
366366
SEQ:
367367
TYPENAME: BlockHeight
368+
- create_network_actions: BOOL
368369
ChainInfoResponse:
369370
STRUCT:
370371
- info:

0 commit comments

Comments
 (0)