Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow subsystems using priority channels #77

Merged
merged 16 commits into from
Jun 7, 2024

Conversation

AndreiEres
Copy link
Contributor

@AndreiEres AndreiEres commented May 30, 2024

Currently subsystems use MeteredReceiver { priority_channel: None }. In this PR we add priority receiver to it MeteredReceiver { priority_channel: Some(Receiver) } and additional sending interface. It should help us prioritize some messages in subsystems, like PeerViewChange (see paritytech/polkadot-sdk#577).

@AndreiEres AndreiEres requested review from alexggh and sandreim May 30, 2024 17:30
@drahnr
Copy link
Collaborator

drahnr commented May 31, 2024

The referenced issue outlines an approach using a generic function, I assume that doesn't compile? Otherwise I'd favor that over the current approach.

@AndreiEres
Copy link
Contributor Author

The referenced issue outlines an approach using a generic function, I assume that doesn't compile? Otherwise I'd favor that over the current approach.

@drahnr Thank you for the review!
I assume you mean this one #8. Then yes, it's only partially implemented: added only priority_send as a companion to existing send.

self. #channel_name .send(
#support_crate ::make_packet(signals_received, #maybe_boxed_send)
).await.map_err(|_| stringify!( #channel_name ))
if matches!(priority, ChannelsOutPriority::High) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Should you add the same logic for try_send for completeness ?

@@ -635,9 +635,10 @@ pub(crate) fn impl_feature_gated_items(
#(
let (#channel_name_tx, #channel_name_rx)
=
#support_crate ::metered::channel::<
#support_crate ::metered::channel_with_priority::<
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: Should we make this configurable per-subsystem ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We can, but I think we'll have little to gain considering the number of subsystems.

Copy link
Collaborator

Choose a reason for hiding this comment

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

At least from Polkadot perspective, we only need the priority channels for the networking subsystems, so it makes sense to have it configurable per subsystem.

Copy link
Collaborator

@drahnr drahnr Jun 4, 2024

Choose a reason for hiding this comment

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

I'd be in favor of having it per subsystem or for everything. We will poll twice as many channels, in the empty priority channel case, which will be the default for most I presume.

@drahnr
Copy link
Collaborator

drahnr commented Jun 3, 2024

Is it expected to ever have the priority defined at runtime? My hunch is no. I'd hence would like to see something like fn channel<P: Priority>(..) { ..} where trait Priority {} (private!) is implemented for pub struct Low;, pub struct High;.

@drahnr
Copy link
Collaborator

drahnr commented Jun 3, 2024

Note that I consider this solution an OK solution. Since at any point in time a peerview is atomic per peer, we could do an aggregate state and only share a reference update to it. This is out of scope for orchestra, even goes against the design principles, but it might make sense to re-think it being a message in the first place given the context provided in this issue. A full solution could work with a notification future being Poll::Ready each time the shared per peer state got updated, but aliases on multiple updates. Not sure this makes sense to you or can be applied 1:1.

orchestra/tests/subsystems_priority_channels_test.rs Outdated Show resolved Hide resolved
/// Tries to send a direct priority message to some other `Subsystem`, routed based on message type.
/// This method is useful for cases where the message queue is bounded and the message is ok
/// to be dropped if the queue is full. If the queue is full, this method will return an error.
/// This method is not async and will not block the current task.
Copy link
Collaborator

Choose a reason for hiding this comment

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

It doesn't make much sense to drop priority messages.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Make sense. Does it mean that we should not have the try_priority_send at all?

@sandreim
Copy link
Collaborator

sandreim commented Jun 3, 2024

Is it expected to ever have the priority defined at runtime? My hunch is no. I'd hence would like to see something like fn channel<P: Priority>(..) { ..} where trait Priority {} (private!) is implemented for pub struct Low;, pub struct High;.

That sounds like a good idea, making things more generic, but multiple priorities means multiple underlying channels which si what #8 proposes. At this point I am not convinced that having so many priority levels is actually that useful for Polkadot. @drahnr do you have any practical usecase in mind that needs this right now ? If not, I guess we can just be happy with this very simple thing with just 2 priorities.

@sandreim
Copy link
Collaborator

sandreim commented Jun 3, 2024

A full solution could work with a notification future being Poll::Ready each time the shared per peer state got updated, but aliases on multiple updates. Not sure this makes sense to you or can be applied 1:1.

I like this approach in principle: moving this sync mechanism out of band. But wouldn't it actually perform worse by having so many (up to 1k) notification futures, compared to what we want to do ?

@drahnr
Copy link
Collaborator

drahnr commented Jun 3, 2024

I like this approach in principle: moving this sync mechanism out of band. But wouldn't it actually perform worse by having so many (up to 1k) notification futures, compared to what we want to do ?

Depends on the update frequency, but I think given the context the back of the envelope will proof you're right @sandreim

@AndreiEres AndreiEres requested review from sandreim, alexggh and drahnr June 5, 2024 11:16
@drahnr
Copy link
Collaborator

drahnr commented Jun 5, 2024

I like this approach in principle: moving this sync mechanism out of band. But wouldn't it actually perform worse by having so many (up to 1k) notification futures, compared to what we want to do ?

I smell a custom queue with aggregatable values, and addressable by keys (here peer Ids) would be what could make this work efficiently, while also limiting the length to the number of peer views. </nerd snipe>

).await;
}

async fn priority_send_message(&mut self, msg: OutgoingMessage)
Copy link
Collaborator

Choose a reason for hiding this comment

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

If we use the generic with default value, we could avoid this additional public API

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Do you mean we should make priority markers public? Considering, that I couldn't set defaults it's a bit inconvenient to write them every time.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Hmm, in that case I propose a slighly different API send_message and send_message_with_priority<Priority> which is called by send_message.

)
),
ChannelsOutPriority::Normal,
).map_err(|err| match err {
Copy link
Collaborator

@drahnr drahnr Jun 5, 2024

Choose a reason for hiding this comment

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

Same as above, avoid the code duplication of .map_err, here an intermediate inline fn would help

@@ -81,14 +89,21 @@ pub(crate) fn impl_channels_out_struct(info: &OrchestraInfo) -> Result<proc_macr
&mut self,
signals_received: usize,
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'd like to see a unit test regarding how signals_received is handled and how that effects the priority vs non-priority messages.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added signals to tests

Copy link
Collaborator

@drahnr drahnr left a comment

Choose a reason for hiding this comment

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

See inline, a few open questions/suggestions/nits

@AndreiEres AndreiEres requested a review from drahnr June 6, 2024 14:11
@drahnr
Copy link
Collaborator

drahnr commented Jun 7, 2024

@AndreiEres #77 (comment) is really my remaining concern, everthing else LGTM

@AndreiEres
Copy link
Contributor Author

@AndreiEres #77 (comment) is really my remaining concern, everthing else LGTM

Thank you, that's updated here 03fae37

Copy link
Collaborator

@sandreim sandreim left a comment

Choose a reason for hiding this comment

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

Thanks @AndreiEres

@drahnr drahnr merged commit 3024e64 into master Jun 7, 2024
7 checks passed
@AndreiEres AndreiEres deleted the AndreiEres/subsystems-priority-send branch June 10, 2024 07:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants