0

I'm have implemented node grpc client retry using interceptor but it is only retrying once and not upto maxRetries which is specified. Also, the error is not propagating to the caller. Can you please where i'm missing in fixing this?

function createRetryInterceptor(maxRetries = 3, backoffInterval) {
  return function (options, nextCall) {
    let retries = 0;

    return new grpc.InterceptingCall(nextCall(options), {
      start: function (metadata, listener, next) {
        function retryOrNext(status) {
          if (status.code === grpc.status.UNAVAILABLE && retries < maxRetries) {
            retries++;

              console.log(`Retry ${retries} after ${backoffInterval}ms`);
              console.error('Error message:', status.details);

            // Create a new call with the same options
            const call = nextCall(options);
            call.start(metadata, listener, retryOrNext);

            // Add a timeout for retry
            setTimeout(() => {
              call.cancel(); // Cancel the retry call if it takes too long
            }, backoffInterval);
          } else {
            // Propagate errors from interceptor as well
            if (status.code !== grpc.status.OK) {
              next(status);
              //return; // Exit the retry loop if not OK or UNAVAILABLE
            }
            // Proceed with successful call or other non-retriable errors
            next(status);
          }
        }

        // Wrap the listener to handle potential errors in onReceiveStatus
        const wrappedListener = {
          onReceiveStatus: function (status, next) {
            try {
              retryOrNext(status);
            } catch (error) {
              console.error('Error in onReceiveStatus:', error);
              next(status);
            }
          },
          // Forward other listener methods if present
          ...listener,
        };

        next(metadata, wrappedListener);
      }
    });
  };
}

static promiseBuilderFn(resolve, reject) {
    return (err, response) => {
      if (err != null) {
        console.log(`error in calling promise err: ${err}`);
        reject(err);
      } else {
        console.log(`success: ${response}`);
        resolve(response);
      }
    };
  }

The grpc client i have created is like below

const client = new MyClient(
  'localhost:5001',
  grpc.credentials.createInsecure(),
    {interceptors: [createRetryInterceptor(3, 5000)]}
);

and i'm calling the grpc service method as follows

return new Promise((resolve, reject) => {
      client.getData(
        request,
        metadata,
        promiseBuilderFn(resolve, reject)
      );
    })

I tried implementing the interceptor as mentioned in https://github.com/grpc/proposal/blob/master/L5-node-client-interceptors.md#examples but it is giving error as TypeError: nextCall is not a function

1 Answer 1

0

Your retryOrNext function closes over the next parameter to start, so that is what it calls when it doesn't retry. What you actually want is to call the next parameter to onReceiveStatus. You can do that by having retryOrNext itself take a next parameter and call that, and pass that in when calling it from onReceiveStatus.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.