-
Notifications
You must be signed in to change notification settings - Fork 292
refactor(iroh): Re-batch datagrams inside RelayTransport
instead of the ActiveRelayActor
#3421
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
Conversation
Documentation for this PR has been generated and is available at: https://n0-computer.github.io/iroh/pr/3421/docs/iroh/ Last updated: 2025-08-20T12:55:08Z |
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.
I'm not 100% sure about my comments. But the Datagram
semantics don't currently match it's docs. And I think if my comments aren't completely wild it could end up with easier state in all places.
/// the batch with at most `num_segments` and leaving only the rest in `self`. | ||
/// | ||
/// Calling this on a datagram batch that only contains a single datagram (`segment_size == None`) | ||
/// will result in returning essentially `Some(self.clone())`, while making `self` empty afterwards. | ||
/// will result in returning essentially a clone of `self`, while making `self` empty afterwards. |
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.
One problem I have with this behaviour is that a segment_size of something with a empty contents (or even with just one datagram) is supposed to be illegal according to the docs of the struct.
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.
Maybe this could be simplified by always having a segement_size
? It's only the serialisation that needs to be linux-compatible. Likewise we can declare that an empty Datagrams
is allows and provide an is_empty
method.
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.
I think the only invariant that's invalidated here is that self.contents
can be empty after this call.
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.
Actually that's not a claimed invariant. Datagrams
doesn't have any restrictions on contents
. We only check that on receive, but that's basically it.
Can you tell me which invariant from the Datagams
documentation is actually invalidated?
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.
Line 141-143 in this file in this PR:
/// The segment size if this transmission contains multiple datagrams.
/// This is `None` if the transmit only contains a single datagram
pub segment_size: Option<NonZeroU16>,
We can change this to segment_size: NonZeroU16
and add to the docs of the contents
field that it might be empty, just should always be a multiple of segment_size
if non-empty. Then this becomes a little more consistent?
Apologies for my vague reviews, I should clearly give more context up front!
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.
In your initial review comment you write:
One problem I have with this behaviour is that a segment_size of something with a empty contents (or even with just one datagram) is supposed to be illegal according to the docs of the struct.
I don't see where it says that?
This is a different invariant:
The segment size if this transmission contains multiple datagrams.
This isNone
if the transmit only contains a single datagram
The current code still respects that.
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.
I think after you call Datagrams::take_segments
this guarantee is not upheld? You can end up with a single item but segment_size
being still Some
or it can be empty, in which case segment_size
can not be right.
Though I appreciate that the way you use it does at least solve the 2nd issue.
Anyway, I don't feel like my comments here are being helpful, or maybe I'm even misunderstanding things. This conversation isn't really improving the quality of the code. Apologies to hold things up.
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.
Ugh, I'm sorry. You're right that I missed a case where the invariant is broken in self
.
I kept double-checking the return value and what's stored in self
after the early-return and missed what's stored in self
after the late return.
Sorry this took so long :X
7712341
to
1af03c2
Compare
/// the batch with at most `num_segments` and leaving only the rest in `self`. | ||
/// | ||
/// Calling this on a datagram batch that only contains a single datagram (`segment_size == None`) | ||
/// will result in returning essentially `Some(self.clone())`, while making `self` empty afterwards. | ||
/// will result in returning essentially a clone of `self`, while making `self` empty afterwards. |
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.
Line 141-143 in this file in this PR:
/// The segment size if this transmission contains multiple datagrams.
/// This is `None` if the transmit only contains a single datagram
pub segment_size: Option<NonZeroU16>,
We can change this to segment_size: NonZeroU16
and add to the docs of the contents
field that it might be empty, just should always be a multiple of segment_size
if non-empty. Then this becomes a little more consistent?
Apologies for my vague reviews, I should clearly give more context up front!
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.
I might be getting something wrong, and this certainly isn't worth holding up the great improvement this is. Apologies for dragging this out.
/// the batch with at most `num_segments` and leaving only the rest in `self`. | ||
/// | ||
/// Calling this on a datagram batch that only contains a single datagram (`segment_size == None`) | ||
/// will result in returning essentially `Some(self.clone())`, while making `self` empty afterwards. | ||
/// will result in returning essentially a clone of `self`, while making `self` empty afterwards. |
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.
I think after you call Datagrams::take_segments
this guarantee is not upheld? You can end up with a single item but segment_size
being still Some
or it can be empty, in which case segment_size
can not be right.
Though I appreciate that the way you use it does at least solve the 2nd issue.
Anyway, I don't feel like my comments here are being helpful, or maybe I'm even misunderstanding things. This conversation isn't really improving the quality of the code. Apologies to hold things up.
… a datagram batch
Sorry, you were completely right to "drag this out", at the end it was me dragging things out because I just kept getting confused about the invariant. I hope the last commit fixed this! |
Description
This does the re-batching inside
RelayTransport
by storing apending_item: Option<RelayRecvDatagram>
.When we
poll_recv
, we first try to use that pending item instead of polling a new one.Once we've got a pending item, we try to split off as much from it as can possibly fit into our receive buffer and handle that.
Breaking Changes
iroh_relay::protos::relay::Datagrams::take_segments
now returnDatagrams
instead ofOption<Datagrams>
, not distinguishing the case whereDatagrams
might be empty.Notes
Probably needs some test.
I'd love to test two
RelayTransport
s talking to each other with differentmax_transmit_segments
/max_receive_segments
, but I'm not sure I can make such a test setup happen easily.Change checklist