mode=grpcwebtext not always triggering "end" event on stream
Closed this issue ยท 7 comments
Hi,
The grpc-web-compatibility-test repo recently automated all its tests (thanks @public ๐). However, this exposed a problem that wasn't obvious when running tests manually:
It seems that streaming methods in the generated grpcwebtext
mode sometimes fail to return successfully. Please see johanbrandhorst/grpc-web-compatibility-test#15 (comment) and johanbrandhorst/grpc-web-compatibility-test#13 for more discussion. Notably, the following can be seen in the console during failures:
index.js:44 Uncaught TypeError: b.i is not a function
at S.<anonymous> (index.js:44)
at sb (index.js:29)
at L (index.js:28)
at Zb (index.js:37)
at S../node_modules/grpc-web/index.js.S.R (index.js:36)
at S../node_modules/grpc-web/index.js.S.K (index.js:36)
(maybe related to #566?).
@public @johanbrandhorst Thanks for the report. Will definitely look into this. You mentioned that this is flaky? In what ways is this flaky? Does it mean that if you run the tests long enough (i.e. streaming and sending lots and lots of messages), that it eventually failed?
No, it seems to fail almost every time in the testing scenario, surprisingly. The streaming call times out. But every now and then (1 in 10 test runs) it passes. That's what I mean by flaky.
Done a bit of research on this today. It's rather hard to reverse engineer whats going on since there's no sourcemaps in the published npm package and I can't get the build to work on my machine.
I am pretty sure that index.js:44 Uncaught TypeError: b.i is not a function
is related but not the root cause.
This is the test case that's failing
const req = new ServerStreamingEchoRequest();
req.setMessageCount(5);
var interval = new Duration();
interval.setSeconds(1);
req.setMessageInterval(interval);
req.setMessage("test");
const srv = client.serverStreamingEcho(req);
var recvCount = 0;
srv.on("data", function(msg: ServerStreamingEchoResponse) {
assert(msg.getMessage() == "test");
recvCount += 1;
});
srv.on("status", function(status: grpcWeb.Status) {
assert(status.code == grpcWeb.StatusCode.OK);
});
srv.on("end", function() {
assert(recvCount == 5);
done();
});
The tests don't actually fail on the b.i
error, they fail on a timeout because done()
is never called from our end
handler.
If we delete the data
event handler we always get the b.i
error. Because the XHR is sent as soon as client.serverStreamingEcho(req);
is called we have to race to get our event handlers attached before the XHR triggers any events. Generally this doesn't seem to happen when running the tests in normal (non-headless) Chrome but makes me think that there's some sort of race condition in the event handling. Is it possible for the XHR to start firing events before we've attached the handlers?
If we move the done
to inside status
instead we don't get any failures. But it seems very suspicious that end
isn't firing consistently in either Chrome or Chrome Headless.
@johanbrandhorst Maybe we should just change our tests to only check for the status event? It's not entirely clear what's the difference in semantics for status vs end in this case.
Maybe we should just change our tests to only check for the status event?
@public This sounds fine for our tests, but obviously we'll leave this issue open to clarify the interactions between status
and end
events.
@public, thanks for the investigation. In that case, this sounds very similar to issues like #289 and #543 where we had observed that the end
event were not being fired by the browser, potentially due to some kind of race conditions. In the past users have used the workaround of listening to the status
event, but I do want to understand the issue with end
, although we haven't figured that one out yet.
Closing this as duplicate of #543
The issue will be solve by just add the empty functions stream.on("end", function (){})