-
Notifications
You must be signed in to change notification settings - Fork 1k
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
feat(codec): Introduce Decoder/Encoder
traits
#208
Conversation
@alce friendly ping, anything I can help here? |
b5c473d
to
3453fd5
Compare
So I brought this branch inline with latest master and ran benchmarks:
Old is #6673f9 and new is this branch with a merge of that commit. It looks like it may not be worth it or we should spend some more time on this. |
16f4aae
to
4728f24
Compare
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.
Perfect! Thanks!
Decoder/Encoder
traits
I haven't dug into this much, but while updating to tonic 0.1 this has bitten ne as it seems this is preventing me from doing zero-copy handling of Bytes like I used to do by hiding the Bytes/BytesMut type. I previously used tokio's BytesCodec and passed buffers (containing a flatbuffers payload) to tonic in a zero-copy manner. Has this ability been lost? |
Here's the raw byte codec I was using on tonic beta. I was mostly delegating to However I had the decoder do zero-copy splitting of BytesMut -- e.g the byte buffer returned by tonic was reused in my application code, and that's something that is not possible with the new Previously: #[derive(Clone, Copy, Default, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub(crate) struct FlatbuffersCodec(());
impl FlatbuffersCodec {
pub(crate) const fn new() -> Self {
FlatbuffersCodec(())
}
}
impl Codec for FlatbuffersCodec {
type Encode = Bytes;
type Decode = BytesMut;
type Encoder = Self;
type Decoder = Self;
fn encoder(&mut self) -> Self::Encoder {
FlatbuffersCodec::new()
}
fn decoder(&mut self) -> Self::Decoder {
FlatbuffersCodec::new()
}
}
impl Encoder for FlatbuffersCodec {
type Item = Bytes;
type Error = Status;
fn encode(&mut self, item: Self::Item, buf: &mut BytesMut) -> Result<(), Self::Error> {
BytesCodec::new().encode(item, buf).map_err(from_io_error)
}
}
impl Decoder for FlatbuffersCodec {
type Item = BytesMut;
type Error = Status;
fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
BytesCodec::new().decode(buf).map_err(from_io_error)
}
}
fn from_io_error(error: std::io::Error) -> Status {
// Map Protobuf parse errors to an INTERNAL status code, as per
// https://github.com/grpc/grpc/blob/master/doc/statuscodes.md
Status::new(Code::Internal, error.to_string())
} With the new trait design: #[derive(Clone, Copy, Default, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub(crate) struct FlatbuffersCodec(());
impl FlatbuffersCodec {
pub(crate) const fn new() -> Self {
FlatbuffersCodec(())
}
}
impl Codec for FlatbuffersCodec {
type Encode = Bytes;
type Decode = BytesMut;
type Encoder = Self;
type Decoder = Self;
fn encoder(&mut self) -> Self::Encoder {
FlatbuffersCodec::new()
}
fn decoder(&mut self) -> Self::Decoder {
FlatbuffersCodec::new()
}
}
impl Encoder for FlatbuffersCodec {
type Item = Bytes;
type Error = Status;
fn encode(&mut self, item: Self::Item, buf: &mut EncodeBuf<'_>) -> Result<(), Self::Error> {
// this is the same as before
buf.reserve(item.len());
buf.put(item);
Ok(())
}
}
impl Decoder for FlatbuffersCodec {
type Item = BytesMut;
type Error = Status;
fn decode(&mut self, buf: &mut DecodeBuf<'_>) -> Result<Option<Self::Item>, Self::Error> {
if buf.remaining() > 0 {
let mut res = BytesMut::with_capacity(buf.remaining()); // This is now an extra alloc
res.put_slice(buf.bytes());
Ok(Some(res))
} else {
Ok(None)
}
}
} |
@magnet did you find a solution to this? |
I haven't, but I have been busy with other stuff since. |
Any chance you are going to continue with it @magnet? This is a pretty awesome addition. I'm assuming that the new trait design totally prohibits you though. Would Tonic need changes to make this possible? |
Not in the short term, no, maybe I'll pick this up sometimes next year. |
Pull request hyperium#208 introduced `tonic::codec::{DecodeBuf, Decoder, EncodeBuf, Encoder}` in preparation for experimenting with a different decoding strategy, but as it currently stands the in-tree functionality is equivalent to the original implementation using `tokio_util::codec::{Decoder, Encoder}` and `bytes::BytesMut`. Given that a significant amount of time has passed with no experimentation having happened (as far as I can tell), this commit reverts those changes. Additionally, it also restores efficient use of the underlying buffers, an issue that was brought up later in its respective comment thread.
In preparation for experimenting with a different decoding strategy, this PR swaps tokio's
Decoder
andEncoder
traits with our own.