Comments (10)
@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.
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.
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.
I'm still able to reproduce this in my specific application, so the patch above isn't a fix.
from tonic.
I can reproduce with blobservice
if I run the client in a loop.
from tonic.
@seanmonstar would hyper be cutting the body off by chance?
from tonic.
I wouldn't expect it to. Could try wireshark or something to sniff what DATA
frames are sent/received.
from tonic.
Cool I'll look at it with Wireshark.
from tonic.
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.
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:
- Tonic polls for data, gets
N
bytes, wherelength <= N < 5 + length
- Tonic sees that
N
bytes have been read, and ifN >= length
, advances the buffer by 5 bytes - Advancing the buffer reduces the buffer length by 5, now we have that
length - 5 <= N < length
- Since we need
length
bytes for the proto, prost fails with a buffer underflow
from tonic.
Related Issues (20)
- Malformed response for non `Ok` statuses when using `web_tonic` on the server.
- Dynamically named services
- Bump hyper / http / http-body to 1.x version HOT 2
- Integration of flatbuffers
- gRPC-web CORS issue HOT 1
- Add a way to test/inspect server and router build/configuration
- GOAWAY message leads to other Clients/Channels being unresponsive
- `tonic-reflection` is still using deprecated file descriptor set reflection_v1alpha1 HOT 1
- tonic-build: Codegen generates a name collision after auto-capitalization is applied, resulting in cargo build errors HOT 1
- `tonic-health`: add `NOT_SERVING` override for all registered services
- Idiomatic import paths
- IPv6 endpoint passed through the URL will crash
- Async Interceptor support HOT 2
- How to create channel without tls when tls feature flag is enabled HOT 1
- Response::new does not send grpc-status code correctly HOT 1
- Sending data frame followed by trailer frame discards the data frame
- Content-Type Validation Missing: Requests Not Returning HTTP 415 When Content-Type Is Not Start with `application/grpc`
- http2_keep_alive_interval doesn't work HOT 3
- Current "uds" example does not compile HOT 3
- Release 0.12.0 HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from tonic.