gRPC Timeouts & Deadlines

First, let’s figure out how the timeout differs from the deadline.

Timeout is relative value, but deadline is absolute. If we take a timeout and a deadline of 5 seconds, and we will write the following line (the code for an example, it will not work if you run it):

Operation operation = new Operation();
operation.deadline(5000, TimeUnit.MILLISECONDS);
Thread.sleep(4000);
operation.run();

Then, if you want to know your deadline time, you need mentally take time while you calling deadline() method and add value you specified in deadline() method (it is 5 seconds) to it. This value will be the deadline time for operation. For example, if the line:

operation.deadline(5, TimeUnit.SECONDS);

completed at 10:00:00, then the operation deadline will be at 10:00:05.

Then we wait 4 seconds:

Thread.sleep(4000);

And it turns out that to perform the operation:

operation.run();

There will be only 1 second left.

Timeouts work differently. This is a relative value, the counting of which will begin only during the execution of the operation itself:

operation.run();

This is how deadlines differs timeouts in gRPC.

There are three main cases in gRPC where we use timeouts / deadlines on the client side:

  • Timeout when creating a channel to a server
  • Deadline for the execution of the request by the server
  • Timeout when channel shutdown

Creating channel timeout (first point)

Please note that this is a timeout, not a deadline. It is set in this way:

ManagedChannel channel = NettyChannelBuilder.forAddress(host, port)
        .withOption(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000)
        .usePlaintext()
        .build();

This means that if within 5 seconds we failed to connect to the server, there will be an error.

Request deadline execution (second point)

Please note, this is a deadline. Therefore, it is very important to call the remote method IMMEDIATELY AFTER specifying withDeadlineAfter(). That is, first we made a stub:

ServiceGrpc.ServiceGrpcBlockingStub stub = ServiceGrpc.newBlockingStub(channel);

And then we called it with the withDeadlineAfter() method and immediately specified the remote method that we need to call:

stub.withDeadlineAfter(30000, TimeUnit.MILLISECONDS)
        .yourRpcMethod(YourRequest);

It turns out that the request will abort 30 seconds after the call withDeadlineAfter().

If you call the withDeadlineAfter() method when creating a stub, and something happens between the creation and use of the stub, then you will have LESS time to complete the operation than the specified deadline.

It is important to note that if an incoming gRPC request triggers an outgoing one, then the deadline value and the request closed event are inherited. When an inbound gRPC request is interrupted, the outgoing request is also be interrupt. To avoid this, you must use the following construction:

public void myRpcMethod(Request req, StreamObserver<Response> observer) {
  // ctx has all the values as the current context, but
  // won't be cancelled
  Context ctx = Context.current().fork();
  // Set ctx as the current context within the Runnable
  ctx.run(() -> {
    // Can start asynchronous work here that will not
    // be cancelled when myRpcMethod returns
  });
  observer.onNext(generateReply());
  observer.onCompleted();
}

Code copyright Eric Anderson: https://stackoverflow.com/a/57120034.

Close channel timeout (third point)

In the last article I wrote that if we create a channel, then we must close it after use. Also it is important to wait until the channel closes:

channel.shutdown();
try {
    channel.awaitTermination(5000, TimeUnit.MILLISECONDS);
} catch (InterruptedException ex) {
    log.error("gRPC channel shutdown interrupted");
}

Please note that this is a timeout.

Telegram channel

If you still have any questions, feel free to ask me in the comments under this article or write me at promark33@gmail.com.

If I saved your day, you can support me 🤝

Leave a Reply

Your email address will not be published. Required fields are marked *