question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

StreamObservers.copyWithFlowControl calls StreamObserver.onComplete twice

See original GitHub issue

Client stream observer on ready handler is invoked after stream is half closed.

      StreamObserver<HelloReply> resp = new ClientResponseObserver<HelloRequest, HelloReply>() {
        @Override
        public void onNext(HelloReply value) {

        }

        @Override
        public void onError(Throwable t) {
          t.printStackTrace();
        }

        @Override
        public void onCompleted() {
        }

        @Override
        public void beforeStart(ClientCallStreamObserver<HelloRequest> requestStream) {
          StreamObservers.copyWithFlowControl(
              ImmutableList.of(HelloRequest.newBuilder().setName(payload).build()), 
              requestStream);
        }
      };
     stub.sayHello(resp);

Following error is found:

> io.grpc.StatusRuntimeException: CANCELLED: Failed to call onReady.
	at io.grpc.Status.asRuntimeException(Status.java:526)
	at io.grpc.stub.ClientCalls$StreamObserverToCallListenerAdapter.onClose(ClientCalls.java:419)
	at io.grpc.PartialForwardingClientCallListener.onClose(PartialForwardingClientCallListener.java:37)
	at io.grpc.ForwardingClientCallListener.onClose(ForwardingClientCallListener.java:23)
	at io.grpc.ForwardingClientCallListener$SimpleForwardingClientCallListener.onClose(ForwardingClientCallListener.java:40)
	at io.grpc.internal.CensusStatsModule$StatsClientInterceptor$1$1.onClose(CensusStatsModule.java:684)
	at io.grpc.PartialForwardingClientCallListener.onClose(PartialForwardingClientCallListener.java:37)
	at io.grpc.ForwardingClientCallListener.onClose(ForwardingClientCallListener.java:23)
	at io.grpc.ForwardingClientCallListener$SimpleForwardingClientCallListener.onClose(ForwardingClientCallListener.java:40)
	at io.grpc.internal.CensusTracingModule$TracingClientInterceptor$1$1.onClose(CensusTracingModule.java:391)
	at io.grpc.internal.ClientCallImpl.closeObserver(ClientCallImpl.java:471)
	at io.grpc.internal.ClientCallImpl.access$300(ClientCallImpl.java:63)
	at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl.close(ClientCallImpl.java:553)
	at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl.access$600(ClientCallImpl.java:474)
	at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1StreamOnReady.runInContext(ClientCallImpl.java:613)
	at io.grpc.internal.ContextRunnable.run(ContextRunnable.java:37)
	at io.grpc.internal.SerializingExecutor.run(SerializingExecutor.java:123)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.IllegalStateException: call already half-closed
	at com.google.common.base.Preconditions.checkState(Preconditions.java:501)
	at io.grpc.internal.ClientCallImpl.halfClose(ClientCallImpl.java:418)
	at io.grpc.PartialForwardingClientCall.halfClose(PartialForwardingClientCall.java:43)
	at io.grpc.ForwardingClientCall.halfClose(ForwardingClientCall.java:22)
	at io.grpc.ForwardingClientCall$SimpleForwardingClientCall.halfClose(ForwardingClientCall.java:44)
	at io.grpc.PartialForwardingClientCall.halfClose(PartialForwardingClientCall.java:43)
	at io.grpc.ForwardingClientCall.halfClose(ForwardingClientCall.java:22)
	at io.grpc.ForwardingClientCall$SimpleForwardingClientCall.halfClose(ForwardingClientCall.java:44)
	at io.grpc.stub.ClientCalls$CallToStreamObserverAdapter.onCompleted(ClientCalls.java:330)
	at io.grpc.stub.StreamObservers$1FlowControllingOnReadyHandler.run(StreamObservers.java:52)
	at io.grpc.stub.ClientCalls$StreamObserverToCallListenerAdapter.onReady(ClientCalls.java:426)
	at io.grpc.PartialForwardingClientCallListener.onReady(PartialForwardingClientCallListener.java:42)
	at io.grpc.ForwardingClientCallListener.onReady(ForwardingClientCallListener.java:23)
	at io.grpc.ForwardingClientCallListener$SimpleForwardingClientCallListener.onReady(ForwardingClientCallListener.java:40)
	at io.grpc.PartialForwardingClientCallListener.onReady(PartialForwardingClientCallListener.java:42)
	at io.grpc.ForwardingClientCallListener.onReady(ForwardingClientCallListener.java:23)
	at io.grpc.ForwardingClientCallListener$SimpleForwardingClientCallListener.onReady(ForwardingClientCallListener.java:40)
	at io.grpc.internal.ClientCallImpl$ClientStreamListenerImpl$1StreamOnReady.runInContext(ClientCallImpl.java:608)

payload is a string of 1 million char. This error does not happen with small payload.

What version of gRPC are you using?

1.11.0

What did you expect to see?

onComplete() should only be called once for request stream.

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:8 (4 by maintainers)

github_iconTop GitHub Comments

1reaction
carl-mastrangelocommented, Jun 13, 2018

@biran0079 That’s certainly possible. isReady can change spuriously, and its only a hint. Consider the case where another RPC consumers the last of the flow control window between the time you get the onReadyCallback, and when you check isReady.

0reactions
biran0079commented, Jun 27, 2018
Read more comments on GitHub >

github_iconTop Results From Across the Web

StreamObserver (grpc-all 1.51.0 API)
Can be called many times but is never called after onError(Throwable) or onCompleted() are called. Unary calls must invoke onNext at most once....
Read more >
Can someone explain to me what's the proper usage of gRPC ...
Basically, I need too transfer error details from gRPC server to client, but I find it hard to understand the proper usage of...
Read more >
grpc-java/stub/src/test/java/io/grpc/stub/ServerCallsTest.java
Is called twice, once to permit the first message and once again after the first message. // has been processed (auto flow control)....
Read more >
Airframe RPC - wvlet.github.io
Airframe RPC enables calling Scala methods at remote servers. ... StreamObserver // Create an async gRPC client val client = ServiceGrpc.
Read more >
Client cancellation unnoticed by server until it calls ...
Regardless, the server behaves the same: the loop executes 10 times (evidence via log messages), and during the call to streamObserver.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found