rpc Write(stream WriteRequest) returns (stream WriteResponse);
message FileData {
int64 offset = 1;
bytes data = 2;
}
message WriteRequest {
repeated FileData extents = 1;
}
message WriteResponse {
int64 throttle = 1;
}
def Write(self, request_iterator, context):
request = request_iterator.next()
next_request = request
while True:
request = next_request
for extent in request.extents:
# Write data
# This call blocks forever
next_request = request_iterator.next()
if not next_request:
# Do important stuff before client unblocks
yield WriteResponse(throttle=0)
break
else:
yield WriteResponse(throttle=0)
I have a bi-directional gRPC that writes to a file and returns responses. Protobuf is defined roughly like this:file.proto
rpc Write(stream WriteRequest) returns (stream WriteResponse);
message FileData {
int64 offset = 1;
bytes data = 2;
}
message WriteRequest {
repeated FileData extents = 1;
}
message WriteResponse {
int64 throttle = 1;
}The issue I'm running into is that I need to run special logic on the last request in the iterator before returning a response (e.g. flush buffers). The only way I can think to do that is to peek ahead in the iterator
, but iterator.next() blocks forever if I haven't returned a response since the last time I called it.file.py
def Write(self, request_iterator, context):
request = request_iterator.next()
next_request = request
while True:
request = next_request
for extent in request.extents:
# Write data
# This call blocks forever
next_request = request_iterator.next()
if not next_request:
# Do important stuff before client unblocks
yield WriteResponse(throttle=0)
break
else:
yield WriteResponse(throttle=0)All I'm really trying to accomplish is running some logic at the very end of the RPC before the client unblocks. If I was using the Java implementation I would have access to ResponseObserver.onCompleted() but it seems like with Python, RPC completion is just implied through yield-ing the response that corresponds with the last request in the request iterator.Is there some way to do this?
I get the same behavior with next(iterator) vs. iterator.next().
It's blocking forever because the client expects as response before it sends the next request. So when the server asks for the next request in the iterator while the client is waiting for a response, nothing is ever going to happen. This is my assumption anyway, I don't know the implementation details of the request iterator.The issue matters for me because if I don't flush writes to disk before returning the final response the client will potentially read an incomplete file. But I don't want to flush before every response because it will hurt performance.
else:
yield WriteResponse(throttle=0)All I'm really trying to accomplish is running some logic at the very end of the RPC before the client unblocks. If I was using the Java implementation I would have access to ResponseObserver.onCompleted() but it seems like with Python, RPC completion is just implied through yield-ing the response that corresponds with the last request in the request iterator.Is there some way to do this?This sounds like a use case that we definitely didn't anticipate, but... it's still rather surprising that it could matter somehow whether or not something was done before the end of the stream became known versus after the end of the stream became known.I suspect that there's something else going on.