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
Improvements to grpc_byte_stream API and handling. #11905
Conversation
|
|
s->base.next = inproc_slice_byte_stream_next; | ||
s->base.pull = inproc_slice_byte_stream_pull; | ||
s->base.destroy = inproc_slice_byte_stream_destroy; | ||
s->base.vtable = &inproc_slice_byte_stream_vtable; |
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 saves space (1 pointer instead of 3) but increases the dependence length. It was load->jump
but is now load->load->jump
(this applies to all of the byte_stream classes that use the vtable). Are there really enough active instances that the memory savings is important?
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 haven't taken any measurements or anything, but I'd say yes: there will be at least one grpc_byte_stream
instance for each sent and received message. In fact, sometimes there will be more than one byte stream for a given sent or received message -- for example, in a case like the new grpc_cached_byte_stream
class, where one byte stream wraps another one.
Also, note that with the addition of the new shutdown method, it's actually 4-to-1, not 3-to-1.
If there's compelling evidence that this is going to be a performance problem, we can certainly revert it. But I think it actually makes the code cleaner.
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.
There's also a consistency argument: most all types we have have this vtable configuration.
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.
Didn't comment on the changes to the filters bc I am not as familiar with those paths. I will take a look next week, but someone else should do a deeper review of them.
@@ -2768,6 +2760,33 @@ grpc_error *grpc_chttp2_incoming_byte_stream_finished( | |||
return error; | |||
} | |||
|
|||
static void incoming_byte_stream_shutdown(grpc_exec_ctx *exec_ctx, |
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.
Would shutdown ever get called with GRPC_ERROR_NONE? In that case isn't there a chance that we would lose some error fidelity. If bs->remaining_bytes > 0, then grpc_chttp2_incoming_byte_stream_finished
would create an error, but it would get lost and never seen, because it would be unreffed here.
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.
Relatedly, doesn't this fail the invariant that the error (whatever it may be) passed to shutdown will be returned to all future calls to pull? Because this shutdown, resets the stream, which will clear the 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.
No, I think shutdown should never be called with GRPC_ERROR_NONE.
I didn't intend the invariant here to be that all subsequent calls to pull() return the error, only that the next call to pull() returns the error. In practice, once a caller gets an error from pull(), they're not going to call it again. I've attempted to clarify the wording in byte_stream.h about this.
@@ -72,6 +72,7 @@ typedef struct sb_list_entry { | |||
typedef struct { | |||
grpc_byte_stream base; | |||
sb_list_entry *le; | |||
grpc_error *shutdown_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.
Maybe it makes sense to pull this into the base struct? I know grpc_chttp2_incoming_byte_stream
doesn't have it at the moment, but maybe it should based on my other comments
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 thought about this, but I suspect that there will be implementations (such as the one in chttp2) that will want to handle shutdown differently.
src/core/lib/transport/byte_stream.c
Outdated
grpc_error *error = | ||
grpc_byte_stream_pull(exec_ctx, stream->cache->underlying_stream, slice); | ||
if (error == GRPC_ERROR_NONE) { | ||
grpc_slice_buffer_add(&stream->cache->cache_buffer, |
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 am a little confused by this. We are caching it in the cache buffer, but also passing it pack up to the original caller? In what situations would the cache grow large enough that calls to this pull would only draw from the cache and not the underlying byte stream?
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.
Ahh, I think I understand now after reading the comments in the header. IS this all for cases in which someone has reset the buffer?
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.
Yes, resetting the caching byte stream is one case. The other case is where you have multiple grpc_caching_byte_stream
objects using the same grpc_byte_stream_cache
, which is something I'll be doing in the retry code.
grpc_byte_stream_cache *cache; | ||
size_t cursor; | ||
grpc_error *shutdown_error; | ||
} grpc_caching_byte_stream; |
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 see this isn't used anywhere yet. We should add a unit test for it (and maybe the other byte streams if possible) before it actually gets used for future projects.
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.
It's used in http_client_filter. But you're right, a test would be good.
Done (and fixed a bug I found in the process, thus confirming that a test was a good idea).
@dgquintas, can you please look at the filter changes? Thanks! |
|
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.
Small nit. Other than that, LGTM. (But still best to have someone with more filter experience do a pass over those files)
grpc_call_next_op(exec_ctx, elem, calld->send_message_batch); | ||
} | ||
|
||
static char *slice_buffer_to_string(grpc_slice_buffer *slice_buffer) { |
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 seems like a function that belongs in a util file
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 if this is something that is needed in any other context. Let's keep it here for now, but we can move it if we find ourselves needing it in a different place.
|
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.
LGTM for filters
LGTM |
- Add shutdown() method (to be used in forthcoming call combiner code). - Use a vtable instead of storing method pointers in each instance. - Check all callers of pull() to make sure that they are properly handling errors. - Clarify ownership rules and attempt to adhere to them. - Added a new grpc_caching_byte_stream implementation, which is used in http_client_filter to avoid having to read the whole send_message byte stream before passing control down the stack. (This class may also be used in the retry code I'm working on separately.) - As part of this, did a major rewrite of http_client_filter, which made the code more readable and fixed a number of potential bugs. Note that some of this code is hard to test right now, due to the fact that the send_message byte stream is always a slice_buffer stream, for which next() is always synchronous and no destruction is needed. However, some future work (specifically, my call combiner work and Craig's incremental send work) will start leveraging this.
8aafcfe
to
5794061
Compare
handling errors.
http_client_filter to avoid having to read the whole send_message byte
stream before passing control down the stack. (This class may also be
used in the retry code I'm working on separately.)
made the code more readable and fixed a number of potential bugs.
Note that some of this code is hard to test right now, due to the fact
that the send_message byte stream is always a slice_buffer stream, for
which next() is always synchronous and no destruction is needed.
However, some future work (specifically, my call combiner work and
Craig's incremental send work) will start leveraging this.
@vjpai Please look at the inproc_transport code.
@ctiller Please carefully scrutinize the chttp2 code.
@makdharma FYI, you may want to look at the changes in http_client_filter.