-
Notifications
You must be signed in to change notification settings - Fork 2.8k
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
Remove p2pservice networkcodec trait #2388
base: master
Are you sure you want to change the base?
Conversation
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.
Generally I like the decoupling you're achieving here. I think we could do some simplifications by reducing the number of traits, and for example just having one GossipSubCodec
trait and oneRequestResponseReadWrite
trait. Both of these could be generic over the data format if needed.
crates/services/p2p/src/codecs.rs
Outdated
trait DataFormatCodec { | ||
type Error; | ||
fn deserialize<'a, R: Deserialize<'a>>( | ||
encoded_data: &'a [u8], | ||
) -> Result<R, Self::Error>; | ||
|
||
fn serialize<D: Serialize>(data: &D) -> Result<Vec<u8>, Self::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.
Are we sure there are no data formats that take parameters? I'd err on the cautious side and accept a self
parameter to these methods. This would for example allow a dynamically configurable formalt such as:
enum MyFormat {
Postcard,
Json
}
While I don't know by heart when or where we'd like to use this, it feels overly restrictive to explicitly rule it out. This also eliminates the need for `PhantomData` markers in our structs, which is a bit of code smell to me.
trait DataFormatCodec { | |
type Error; | |
fn deserialize<'a, R: Deserialize<'a>>( | |
encoded_data: &'a [u8], | |
) -> Result<R, Self::Error>; | |
fn serialize<D: Serialize>(data: &D) -> Result<Vec<u8>, Self::Error>; | |
} | |
trait DataFormatCodec { | |
type Error; | |
fn deserialize<'a, R: Deserialize<'a>>( | |
&self, | |
encoded_data: &'a [u8], | |
) -> Result<R, Self::Error>; | |
fn serialize<D: Serialize>(&self, data: &D) -> Result<Vec<u8>, Self::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.
|
||
#[derive(Debug, Clone)] | ||
pub struct BoundedCodec<Format> { | ||
_format: PhantomData<Format>, |
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.
Following my other suggestion I'd suggest skipping the PhantomData
here. If the Format
is an empty struct, there's no cost to holding it directly.
_format: PhantomData<Format>, | |
_format: Format, |
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.
/// If the substream was not properly closed when dropped, the sender would instead | ||
/// run into a timeout waiting for the response. | ||
#[async_trait] | ||
impl<Format> request_response::Codec for BoundedCodec<Format> |
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.
The naming feels off here. A trait named Codec
should have encode/decode or serialize/deserialize methods. This trait contains read/write methods and seem to be representing primarily I/O operations. I get that this comes from p2p, but perhaps we should find another name for BoundedCodec
. Right now I'd expect some sort of similarity between the BoundedCodec
and UnboundedCodec
types, but they feel very different in what they do with the current implementations.
Perhaps I'm just not understanding this well enough right now. I'd love to hear your thoughts on this.
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.
Agreed, I had a lot of troubles trying to find the right terminology (I also did not like BoundedCodec
and UnboundedCodec
. In the last version I have made the following changes:
DataFormatCodec
has been replaced byEncode
andDecode
traits,BoundedCodec
is now aRequestResponseMessageHandler
. I still don't like the Handler part in the name as it might make people think that it's something akin to a task, but I could not find anything better. Thedata_format
field has been renamed tocodec
UnboundedCodec
is nowGossipsubMessageHandler
, and thedata_format
field has been renamed tocodec
,- The
Format
type parameter has been renamed tocodec
. - I did not rename the
GossipsubCodec
trait, suggestions for better names are very welcome.
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.
gossipsub_codec: UnboundedCodec<PostcardDataFormat>, | ||
request_response_codec: BoundedCodec<PostcardDataFormat>, |
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.
Given that these are the only two instances of using these codecs, perhaps we could simplify the implementation with more specific traits. What if we just renamed UnboundedCodec
-> GossipsubCodec
(perhaps we only need one trait and not two traits here), and renamed BoundedCodec
-> RequestResponseReadWrite
(or something similar)?
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.
crates/services/p2p/src/codecs.rs
Outdated
use std::io; | ||
|
||
trait DataFormatCodec { |
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.
How about just renaming this DataFormat
?
crates/services/p2p/src/codecs.rs
Outdated
trait DataFormatCodec { | ||
type Error; | ||
fn deserialize<'a, R: Deserialize<'a>>( | ||
encoded_data: &'a [u8], |
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.
This could be generalized to a impl std::io::Read
.
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 sure I follow completely here, do you mean changing the type of econded_data
? If we do so, we need to allocate a second buffer where to read the value into. Or do you have something else in mind?
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 sure I follow completely here, do you mean changing the type of econded_data?
Yes, the suggestion was taking encoded_data: impl std::io::Read + 'a
, instead of explicitly taking a slice here since it allows users to pass in any reader, which is more flexible. However, I see that we use AsyncRead
and not Read
for the network communication which would be a bit more messy so there's no immediate benefit in doing this right now anyway.
If we do so, we need to allocate a second buffer where to read the value into. Or do you have something else in mind?
That shouldn't be necessary since &[u8]
also implements Read
, so all current calls should still work. Or perhaps I'm misunderstanding you here.
crates/services/p2p/src/codecs.rs
Outdated
encoded_data: &'a [u8], | ||
) -> Result<R, Self::Error>; | ||
|
||
fn serialize<D: Serialize>(data: &D) -> Result<Vec<u8>, Self::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.
This could also be generalized by taking a impl std::io::Write
input which it writes to. Then a convenience function serialize_to_vec
could be provided with this signature.
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.
This has been changed to reflect what's been done in the Storage crate. We now have an Encoder
trait that is used as the type where the result is stored, and this is implemented for Cow<'_, [u8]>
. I could in theory change this to have the input where the serialized data is written passed into the function, but this would create me trouble when I will try to merge the traits in the storage trait and this PR together (see new issue #2403)
Linked Issues/PRs
Closes #2368
Description
In the P2P service, we couple together GossipSub and RequestResponse codecs in a
NetworkCodec
trait. In practice, this coupling is not necessary as the two codecs use different Messages and different logics for encoding/deconding messages. The only thing that they share in common is using Postcard as the data format to serialize and deserialize data, but this is not made clear in the network trait.This PR is a rework of Codecs in the libp2p service, which deletes the
NetworkCodec
trait and therefore the coupling betweenGossipSubCodec
andRequestResponseCodec
. In particular:are moved to a struct
Postcardthat implements the
DataFormat` trait,BoundedCodec
struct that implements theRequestResponse::Codec
trait for anyDataFormat
supersedes the oldPostcardCodec
.UnboundedCodec
struct that implements theGossipsubCodec trait for any
DataFormatsupersedes the old
PostcardCodec`,request_response::protocols
module`,Gossipsub
andRequestResponse
.TODO:
Checklist
Before requesting review
After merging, notify other teams
[Add or remove entries as needed]