grpc-dart icon indicating copy to clipboard operation
grpc-dart copied to clipboard

Retry functionality

Open zs-dima opened this issue 4 years ago • 6 comments

Could be nice to have Retry functionality for the gRPC calls. It could be call parameters to pass retry count and delay. Fore the stream respond - in case of error in the middle of the stream - it could be continuation from previous position ideally (or run stream from beginning at least)

zs-dima avatar Oct 01 '20 13:10 zs-dima

There is a detailed design for gRPC retries (https://github.com/grpc/proposal/blob/master/A6-client-retries.md). I think at least C++ and Java implement it right now. I am not sure about other libraries.

I don't think we will have bandwidth to implement this any time soon, PRs are welcome though.

mraleph avatar Oct 02 '20 10:10 mraleph

The issue is that this effectively makes grpc useless on mobile because the only alternative is to rebuild the entire client which can't be done within your call logic without having it at every single call in code.

And even then it's really hard to make even that work when feeding a stream down.

Given flutter is designed for exactly the use case this is required for and almost all dart usage is flutter is strongly suggest this be prioritized.

JohnGalt1717 avatar Dec 27 '20 02:12 JohnGalt1717

@JohnGalt1717 if this is something you need I suggest contributing a PR - because as I have said we don’t really have bandwidth beyond reviewing PRs and doing small emergency fixes at the moment and it is unlikely the situation will change in the near future.

mraleph avatar Dec 27 '20 11:12 mraleph

I'm using Google Cloud Run for my gRPC service, which dies when the 15-minute maximum timeout limit is reached. (Though up to 60-minute timeouts are available in beta.) This triggers a GrpcError with StatusCode.unavailable. Here's how I'm handling streaming RPC retries with the RetryWhenStream class from the RxDart package:

import 'package:grpc/grpc.dart';
import 'package:rxdart/rxdart.dart';
import 'package:logging/logging.dart';

final streamSubscription = RetryWhenStream(
      () => myGrpcClient.myApiCall(request),
      (dynamic error, StackTrace s) {
        if (error is GrpcError) {
          log.warning(error.toString());

          switch (error.code) {
            case StatusCode.unavailable:
            case StatusCode.unknown:
              return Stream.value(null);
            case StatusCode.unauthenticated:
              return () async* {
                // handle token refresh, etc.
                yield null;
              }();
          }
        }

        return Stream.error(error, s);
      },
    ).listen(
      (event) {
        // do something with api response / event
        log.info("New event received");
      },
      onError: (Object error, StackTrace s) =>
          log.severe(error.toString, error, s),
      onDone: () => log.warning("Stream ended"),
    );

I tested it by letting it run in my Flutter app overnight and it worked great!

Something similar could probably be achieved for unary RPC with the Retry package.

🤓

bgetsug avatar Feb 05 '21 15:02 bgetsug

Hope I'm not too late, but I've also encountered the need of using some sort of retry mechanism in both streams and unary calls.

For streams, the solution described by @bgetsug was perfect and needed only minor modifications to fit my requirements. For unary calls however, the RxDart package couldn't be used, but I did find the Retry package which provides the needed retry calls. While in their example, they show a normal http call, the same logic also works for gRPC unary calls.

Here is an example of how I used the Retry package with gRPC: Proto file:

service BackendInterface
{
  rpc GetConfiguration (Empty) returns (ConfigurationMessage);
}

message Empty
{
}

message ConfigurationMessage
{
  string configuration = 1;
}

gRPC unary call:

Future<ConfigurationMessage> get configuration async {
    return await retry(() => backendInterfaceClient
        .getConfiguration(Empty())
        .timeout(const Duration(seconds: 5)));
  }

The above is a simplified example, you can use retryIf and onRetry callbacks to retry only on certain Exceptions.

m-chirodea avatar Oct 11 '22 07:10 m-chirodea

@m-chirodea you could check out https://pub.dev/packages/fresh_grpc for some inspiration perhaps

or https://github.com/grpc/grpc-dart/pull/489#issuecomment-1117204933

Hope this helps.

baku-apps avatar Oct 12 '22 14:10 baku-apps