-
Notifications
You must be signed in to change notification settings - Fork 68
Avoid blocking tokio::select
branches on a potent. full channel
#705
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,10 +21,31 @@ macro_rules! ensure { | |
}; | ||
} | ||
|
||
#[macro_export] | ||
macro_rules! try_fut_and_permit { | ||
($fut:expr, $sender:expr) => { | ||
futures::future::TryFutureExt::unwrap_or_else( | ||
futures::future::try_join( | ||
$fut, | ||
futures::TryFutureExt::map_err($sender.reserve(), |_e| { | ||
SubscriberError::ClosedChannel(stringify!(sender).to_owned()) | ||
}), | ||
), | ||
|e| { | ||
tracing::error!("{e}"); | ||
panic!("I/O failure, killing the node."); | ||
}, | ||
) | ||
}; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Lets stick this macro in the utils crate, and re-use it in places in Sui where we risk blocking select loops. |
||
|
||
pub type SubscriberResult<T> = Result<T, SubscriberError>; | ||
|
||
#[derive(Debug, Error, Clone)] | ||
pub enum SubscriberError { | ||
#[error("channel {0} closed unexpectedly")] | ||
ClosedChannel(String), | ||
|
||
#[error("Storage failure: {0}")] | ||
StoreError(#[from] StoreError), | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,11 @@ | ||
// Copyright (c) 2022, Mysten Labs, Inc. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
use crate::errors::{SubscriberError, SubscriberResult}; | ||
use consensus::ConsensusOutput; | ||
use futures::{ | ||
future::try_join_all, | ||
stream::{FuturesOrdered, StreamExt}, | ||
use crate::{ | ||
errors::{SubscriberError, SubscriberResult}, | ||
try_fut_and_permit, | ||
}; | ||
use consensus::ConsensusOutput; | ||
use futures::{future::try_join_all, stream::FuturesOrdered, FutureExt, TryStreamExt}; | ||
use store::Store; | ||
use tokio::{ | ||
sync::{ | ||
|
@@ -14,7 +14,6 @@ use tokio::{ | |
}, | ||
task::JoinHandle, | ||
}; | ||
use tracing::debug; | ||
use types::{BatchDigest, ReconfigureNotification, SerializedBatchMessage}; | ||
|
||
#[cfg(test)] | ||
|
@@ -83,12 +82,9 @@ impl Subscriber { | |
loop { | ||
tokio::select! { | ||
// Receive the ordered sequence of consensus messages from a consensus node. | ||
Some(message) = self.rx_consensus.recv() => { | ||
(Some(message), permit) = try_fut_and_permit!(self.rx_consensus.recv().map(Ok), self.tx_batch_loader) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So @huitseeker should we expect on every iteration to try and acquire a permit for the requested channel? I am trying to wrap my head around of how the underlying join will work here on the select branch. |
||
// Send the certificate to the batch loader to download all transactions' data. | ||
self.tx_batch_loader | ||
.send(message.clone()) | ||
.await | ||
.expect("Failed to send message ot batch loader"); | ||
permit.send(message.clone()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I confirmed the answer to my own question above is "yes". All is good! |
||
|
||
// Wait for the transaction data to be available in the store. We will then forward these | ||
// transactions to the Executor Core for execution. | ||
|
@@ -98,11 +94,8 @@ impl Subscriber { | |
}, | ||
|
||
// Receive here consensus messages for which we have downloaded all transactions data. | ||
Some(message) = waiting.next() => { | ||
if self.tx_executor.send(message?).await.is_err() { | ||
debug!("Executor core is shutting down"); | ||
return Ok(()); | ||
} | ||
(Some(message), permit) = try_fut_and_permit!(waiting.try_next(), self.tx_executor) => { | ||
permit.send(message) | ||
}, | ||
|
||
// Check whether the committee changed. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,10 +23,31 @@ macro_rules! ensure { | |
}; | ||
} | ||
|
||
#[macro_export] | ||
macro_rules! try_fut_and_permit { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need to define twice? |
||
($fut:expr, $sender:expr) => { | ||
futures::future::TryFutureExt::unwrap_or_else( | ||
futures::future::try_join( | ||
$fut, | ||
futures::TryFutureExt::map_err($sender.reserve(), |_e| { | ||
DagError::ClosedChannel(stringify!(sender).to_owned()) | ||
}), | ||
), | ||
|e| { | ||
tracing::error!("{e}"); | ||
panic!("I/O failure, killing the node."); | ||
}, | ||
) | ||
}; | ||
} | ||
|
||
pub type DagResult<T> = Result<T, DagError>; | ||
|
||
#[derive(Debug, Error)] | ||
pub enum DagError { | ||
#[error("Channel {0} has closed unexpectedly")] | ||
ClosedChannel(String), | ||
|
||
#[error("Invalid signature")] | ||
InvalidSignature(#[from] signature::Error), | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very elegant idea to use the reserve here. I am not fully fluent with macros: this will keep and return what is provided by the reservation (permit) until the send happens right?