GithubHelp home page GithubHelp logo

Comments (10)

cpcloud avatar cpcloud commented on June 28, 2024 1

@seanmonstar After looking at frames using wireshark, I don't see anything anomalous. 10005 bytes are sent as DATA on stream 1, there are WINDOW_UPDATES on streams 0 and 1, then 3 bytes are sent as DATA on stream 1.

I noticed that between the 10005 bytes being sent the tonic decoding thread gets woken up and tries to decode the message before having all the bytes needed to decode it.

from tonic.

cpcloud avatar cpcloud commented on June 28, 2024

After making the following changes I'm now unable to reproduce the issue:

diff --git a/tonic/src/codec/prost.rs b/tonic/src/codec/prost.rs
index 495dc11..5c29a06 100644
--- a/tonic/src/codec/prost.rs
+++ b/tonic/src/codec/prost.rs
@@ -49,12 +49,14 @@ impl<T: Message> Encoder for ProstEncoder<T> {
 
     fn encode(&mut self, item: Self::Item, buf: &mut BytesMut) -> Result<(), Self::Error> {
         let len = item.encoded_len();
+        let additional = prost::encoding::encoded_len_varint(len as u64);
+        let required = len + additional;
 
-        if buf.remaining_mut() < len {
-            buf.reserve(len);
+        if buf.remaining_mut() < required {
+            buf.reserve(required);
         }
 
-        item.encode(buf)
+        item.encode_length_delimited(buf)
             .map_err(|_| unreachable!("Message only errors if not enough space"))
     }
 }
@@ -68,7 +70,7 @@ impl<U: Message + Default> Decoder for ProstDecoder<U> {
     type Error = Status;
 
     fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
-        Message::decode(buf.take())
+        Message::decode_length_delimited(buf.take())
             .map(Option::Some)
             .map_err(from_decode_error)
     }

However, I'm not exactly sure how to reliably test that this changes solves the problem.

from tonic.

cpcloud avatar cpcloud commented on June 28, 2024

but that didn't seem to have any effect when I swapped those calls in in prost.rs

I think I was running a client without the above changes against a server with the changes, which yields the same behavior.

from tonic.

cpcloud avatar cpcloud commented on June 28, 2024

I'm still able to reproduce this in my specific application, so the patch above isn't a fix.

from tonic.

cpcloud avatar cpcloud commented on June 28, 2024

I can reproduce with blobservice if I run the client in a loop.

from tonic.

LucioFranco avatar LucioFranco commented on June 28, 2024

@seanmonstar would hyper be cutting the body off by chance?

from tonic.

seanmonstar avatar seanmonstar commented on June 28, 2024

I wouldn't expect it to. Could try wireshark or something to sniff what DATA frames are sent/received.

from tonic.

cpcloud avatar cpcloud commented on June 28, 2024

Cool I'll look at it with Wireshark.

from tonic.

cpcloud avatar cpcloud commented on June 28, 2024

Looking at the logs coming out of h2 it looks like when 10008 bytes get sent from the server (10000 bytes from the bytes field in the message + 5 bytes for the tonic header + 2 bytes for the encoded length (10000) of the byte array + 1 for the proto tag) the first 10005 make it to the client, and the client tries to decode those 10005 bytes before waiting for the final 3 bytes it needs to fully decode the message.

Is there any reason the decoder would not wait for at minimum the number of bytes required to fully decode a message?

from tonic.

cpcloud avatar cpcloud commented on June 28, 2024

I'm not convinced this is related to threading or h2. I think that the issue most likely lies in tonic.

When receiving data by polling the body, it's possible to end up in a situation where the polling results in a buffer of size N where N >= length, but also that N < 5 + length.

Roughly the flow is like this:

  1. Tonic polls for data, gets N bytes, where length <= N < 5 + length
  2. Tonic sees that N bytes have been read, and if N >= length, advances the buffer by 5 bytes
  3. Advancing the buffer reduces the buffer length by 5, now we have that length - 5 <= N < length
  4. Since we need length bytes for the proto, prost fails with a buffer underflow

from tonic.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.