-
Notifications
You must be signed in to change notification settings - Fork 132
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
RPC Output Stream drops data if AsyncIterator
isn't invoked before response is received
#650
Comments
Another use case is to synchronously push a value in an This breaks: interceptServerStreaming(next, method, input, options) {
let original = next(method, input, options);
let response = new RpcOutputStreamController();
const initialValue = {};
// This message will be dropped
response.notifyMessage(initialValue);
original.response.onNext((message, error, done) => {
if (message) {
response.notifyMessage(message);
}
if (error)
response.notifyError(error);
if (done)
response.notifyComplete();
});
return new ServerStreamingCall(
original.method,
original.requestHeaders,
original.request,
original.headers,
response,
original.status,
original.trailers
);
} The workaround is to use a setTimeout to delay the emission: interceptServerStreaming(next, method, input, options) {
let original = next(method, input, options);
let response = new RpcOutputStreamController();
const initialValue = {};
// This is fine because the message will be sent after the async iterable is subscribed.
setTimeout(() => {
response.notifyMessage(initialValue);
}, 0);
original.response.onNext((message, error, done) => {
if (message) {
response.notifyMessage(message);
}
if (error)
response.notifyError(error);
if (done)
response.notifyComplete();
});
return new ServerStreamingCall(
original.method,
original.requestHeaders,
original.request,
original.headers,
response,
original.status,
original.trailers
);
} |
@jsaguet a better workaround would be to initialize the interceptServerStreaming(next, method, input, options) {
let original = next(method, input, options);
let response = new RpcOutputStreamController();
// initialize the async iterator so that we have an iterator state available
response[Symbol.asyncIterator]();
const initialValue = {};
response.notifyMessage(initialValue);
original.response.onNext(response.notifyNext.bind(response));
return new ServerStreamingCall(
original.method,
original.requestHeaders,
original.request,
original.headers,
response,
original.status,
original.trailers
);
} |
You're absolutely right. Thanks for your help |
The
ServerStreamingCall
'sresponses
is anRpcOutputStream
which has anAsyncIterator
. If the consumer does not synchronously invoke theAsyncIterator
data can be dropped.The above is a race condition. If the service starts sending streaming responses before 1 second had passed then no console log will happen. This is despite the claim:
protobuf-ts/packages/runtime-rpc/src/rpc-output-stream.ts
Lines 18 to 20 in 1798e0d
There is also this:
protobuf-ts/packages/runtime-rpc/src/rpc-output-stream.ts
Line 17 in 1798e0d
However I had always interpreted that to mean that if something had already finished consuming the stream then invoking the
AsyncIterator
a second time would result in no messages. But perhaps when it refers tostream
it means the underlying network call instead of the locally buffered stream.In either case this behavior seems undesirable and non-obvious as it would be akin to saying that if the consumer didn't
await
the unary response before the underlying network call finished that we'd just lose the data. e.g.This behavior also makes it challenging to effectively invoke multiple requests in parallel:
In order for the consumer to actually be successful they would instead need to do this:
I assume the existing behavior might be done in order to avoid extra memory/GC in the off-hand chance that a consumer invoked the streaming call and then simply did nothing with it and/or only cared about the headers or something, but that seems like an odd thing to optimize for.
The text was updated successfully, but these errors were encountered: