Skip to content
This repository has been archived by the owner on Oct 17, 2022. It is now read-only.

Avoid blocking tokio::select branches on a potent. full channel #705

Closed
wants to merge 2 commits into from

Conversation

huitseeker
Copy link
Contributor

@huitseeker huitseeker commented Aug 6, 2022

Checkpointing some (incomplete) backpressure work on top of #691.
This PR is an incremental improvement.

Context

We often have branches of a tokio::select that fire, and then send a message on a channel as part of their immediate processing. This message is sent with sender.send(message).await, which does not return immediately.

The issue

This will yield to the executor, but not let the rest of the select resume.

The solution

We introduce a macro that helps provide semantics by which a select branch fires if it has received an input for its condition (typically a waiting.next() or receiver.recv()) and can acquire a permit to send a message for a downstream channel (typically a sender.send(result)). This gives the other branches in the select a chance to fire in case the downstream channel is full, as the macro-using branch will then not fire. The macro meshes better than an async function with the unusual borrowing behavior of the select macro.

We block on sending to a channel that may be full.
This rather changes the behavior to poll the channel before blocking indefinitely on it.

Percolates backpressure one level up from the output channels of:
- header_waiter,
- certificate_waiter,
@huitseeker huitseeker requested review from bmwill and akichidis August 6, 2022 23:00
@huitseeker huitseeker requested a review from asonnino as a code owner August 6, 2022 23:00
@huitseeker huitseeker changed the title Avoid blocking some tokio::select branches on a potent. full channel Avoid blocking tokio::select branches on a potent. full channel Aug 6, 2022
Copy link
Contributor

@gdanezis gdanezis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a very elegant solution to the tokio::select loop blocking potential issue.

futures::future::TryFutureExt::unwrap_or_else(
futures::future::try_join(
$fut,
futures::TryFutureExt::map_err($sender.reserve(), |_e| {
Copy link
Contributor

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?

.send(message.clone())
.await
.expect("Failed to send message ot batch loader");
permit.send(message.clone());
Copy link
Contributor

Choose a reason for hiding this comment

The 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!

},
)
};
}
Copy link
Contributor

Choose a reason for hiding this comment

The 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.

@@ -23,10 +23,31 @@ macro_rules! ensure {
};
}

#[macro_export]
macro_rules! try_fut_and_permit {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to define twice?

@@ -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) => {
Copy link
Contributor

Choose a reason for hiding this comment

The 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.

Copy link
Contributor

@asonnino asonnino left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIce!

@asonnino asonnino deleted the branch MystenLabs:simplify August 8, 2022 13:16
@asonnino asonnino closed this Aug 8, 2022
@huitseeker
Copy link
Contributor Author

This was closed by mistake, will re-open.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants